Created
May 18, 2015 19:38
-
-
Save egtra/8367b22f11b44ca2cedb to your computer and use it in GitHub Desktop.
非同期ドロップ処理フリースレッドマーシャラー + CFSTR_FILEDESCRIPTORで複数ファイル (Windows Forms + SHCreateMemStream)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define UNICODE | |
#define _UNICODE | |
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 | |
#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 1 | |
#define WINVER 0x0600 | |
#define _WIN32_WINNT 0x0600 | |
#include <string.h> | |
#include <vcclr.h> | |
#include <Windows.h> | |
#include <ShlObj.h> | |
#include <shlwapi.h> | |
#using <System.dll> | |
#using <System.Windows.Forms.dll> | |
using namespace System; | |
using namespace System::Diagnostics; | |
using namespace System::IO; | |
using namespace System::Runtime::InteropServices; | |
using namespace System::Threading; | |
using namespace System::Windows::Forms; | |
namespace ComTypes = System::Runtime::InteropServices::ComTypes; | |
namespace NativeTypes | |
{ | |
[ComVisible(true)] | |
[InterfaceTypeAttribute(ComInterfaceType::InterfaceIsIUnknown)] | |
[GuidAttribute("3D8B0590-F691-11d2-8EA9-006097DF5BD4")] | |
public interface class IDataObjectAsyncCapability | |
{ | |
public: | |
void SetAsyncMode([MarshalAs(UnmanagedType::Bool)] bool fDoOpAsync); | |
[returnvalue:MarshalAs(UnmanagedType::Bool)] | |
bool GetAsyncMode(); | |
void StartOperation(ComTypes::IBindCtx^ pbcReserved); | |
[returnvalue:MarshalAs(UnmanagedType::Bool)] | |
bool InOperation(); | |
void EndOperation(HRESULT hResult, ComTypes::IBindCtx^ pbcReserved, DWORD dwEffects); | |
}; | |
} | |
namespace NativeMethods | |
{ | |
[DllImport("urlmon.dll", PreserveSig = false, ExactSpelling=true)] | |
ComTypes::IEnumFORMATETC^ CreateFormatEnumerator( | |
int cfmtetc, | |
[MarshalAs(UnmanagedType::LPArray, SizeParamIndex=0)] array<ComTypes::FORMATETC>^ rgfmtetc); | |
} | |
static const auto CF_FILEDESCRIPTOR = static_cast<short>(::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR)); | |
static const auto CF_FILECONTENTS = static_cast<short>(::RegisterClipboardFormat(CFSTR_FILECONTENTS)); | |
ref class AppDataObject : public ComTypes::IDataObject, public NativeTypes::IDataObjectAsyncCapability | |
{ | |
public: | |
virtual void GetData([In] ComTypes::FORMATETC% format, [Out] ComTypes::STGMEDIUM% medium) | |
{ | |
if (format.dwAspect != ComTypes::DVASPECT::DVASPECT_CONTENT) | |
{ | |
Marshal::ThrowExceptionForHR(DV_E_DVASPECT); | |
} | |
if (format.cfFormat == CF_FILEDESCRIPTOR) | |
{ | |
auto hGlobal = Marshal::AllocHGlobal(m_fileGroupDescriptorBuffer->Length); | |
Marshal::Copy(m_fileGroupDescriptorBuffer, 0, hGlobal, m_fileGroupDescriptorBuffer->Length); | |
medium.tymed = ComTypes::TYMED::TYMED_HGLOBAL; | |
medium.unionmember = hGlobal; | |
medium.pUnkForRelease = nullptr; | |
Debug::WriteLine("GetData(CFSTR_FILEDESCRIPTOR): " + Thread::CurrentThread->ManagedThreadId); | |
} | |
else if (format.cfFormat == CF_FILECONTENTS) | |
{ | |
if (format.lindex == 0) | |
{ | |
static const BYTE content[] = { 'R', 'i', 'n', 'g', 'o' }; | |
medium.tymed = ComTypes::TYMED::TYMED_ISTREAM; | |
medium.unionmember = static_cast<IntPtr>(SHCreateMemStream(content, sizeof content)); | |
medium.pUnkForRelease = nullptr; | |
Debug::WriteLine("GetData(CFSTR_FILECONTENTS, 0): " + Thread::CurrentThread->ManagedThreadId); | |
} | |
else if (format.lindex == 1) | |
{ | |
static const BYTE content[] = { 'G', 'o', 'r', 'i', 'r', 'a' }; | |
medium.tymed = ComTypes::TYMED::TYMED_ISTREAM; | |
medium.unionmember = static_cast<IntPtr>(SHCreateMemStream(content, sizeof content)); | |
medium.pUnkForRelease = nullptr; | |
Debug::WriteLine("GetData(CFSTR_FILECONTENTS, 1): " + Thread::CurrentThread->ManagedThreadId); | |
} | |
else | |
{ | |
Marshal::ThrowExceptionForHR(DV_E_TYMED); | |
} | |
} | |
else | |
{ | |
WCHAR formatName[1024]{}; | |
GetClipboardFormatNameW(static_cast<USHORT>(format.cfFormat), formatName, ARRAYSIZE(formatName)); | |
Debug::WriteLine("Unknown format: " + gcnew String(formatName)); | |
Marshal::ThrowExceptionForHR(DV_E_TYMED); | |
} | |
} | |
virtual void GetDataHere([In] ComTypes::FORMATETC%, ComTypes::STGMEDIUM%) | |
{ | |
throw gcnew NotImplementedException(); | |
} | |
virtual int QueryGetData([In] ComTypes::FORMATETC%) | |
{ | |
return E_NOTIMPL; | |
} | |
virtual int GetCanonicalFormatEtc([In] ComTypes::FORMATETC%, [Out] ComTypes::FORMATETC%) | |
{ | |
return E_NOTIMPL; | |
} | |
virtual void SetData([In] ComTypes::FORMATETC%, [In] ComTypes::STGMEDIUM%, bool) | |
{ | |
throw gcnew NotImplementedException(); | |
} | |
virtual ComTypes::IEnumFORMATETC^ EnumFormatEtc(ComTypes::DATADIR direction) | |
{ | |
return NativeMethods::CreateFormatEnumerator(m_formatetc->Length, m_formatetc); | |
} | |
virtual int DAdvise([In] ComTypes::FORMATETC%, ComTypes::ADVF, ComTypes::IAdviseSink^, [Out] int%) | |
{ | |
return E_NOTIMPL; | |
} | |
virtual void DUnadvise(int) | |
{ | |
throw gcnew NotImplementedException(); | |
} | |
virtual int EnumDAdvise([Out] ComTypes::IEnumSTATDATA^%) | |
{ | |
return E_NOTIMPL; | |
} | |
virtual void SetAsyncMode([MarshalAs(UnmanagedType::Bool)] bool fDoOpAsync) | |
{ | |
if (fDoOpAsync == false) | |
{ | |
throw gcnew ArgumentException(); | |
} | |
} | |
[returnvalue:MarshalAs(UnmanagedType::Bool)] | |
virtual bool GetAsyncMode() | |
{ | |
return true; | |
} | |
virtual void StartOperation(ComTypes::IBindCtx^ /*pbcReserved*/) | |
{ | |
Debug::WriteLine("StartOperation: " + Thread::CurrentThread->ManagedThreadId); | |
Interlocked::Increment(m_inOperationCount); | |
} | |
[returnvalue:MarshalAs(UnmanagedType::Bool)] | |
virtual bool InOperation() | |
{ | |
return m_inOperationCount > 0; | |
} | |
virtual void EndOperation(HRESULT /*hResult*/, ComTypes::IBindCtx^ /*pbcReserved*/, DWORD /*dwEffects*/) | |
{ | |
Debug::WriteLine("EndOperation: " + Thread::CurrentThread->ManagedThreadId); | |
Interlocked::Decrement(m_inOperationCount); | |
} | |
AppDataObject() | |
{ | |
String^ filename0 = "ringo.txt"; | |
String^ filename1 = "gorira.txt"; | |
UINT numOfFile = 2; | |
m_fileGroupDescriptorBuffer = gcnew array<BYTE>(offsetof(FILEGROUPDESCRIPTOR, fgd) + sizeof (FILEDESCRIPTOR) * numOfFile); | |
pin_ptr<BYTE> buffer = &m_fileGroupDescriptorBuffer[0]; | |
auto pfgd = reinterpret_cast<FILEGROUPDESCRIPTOR*>(buffer); | |
pfgd->cItems = numOfFile; | |
pfgd->fgd[0].dwFlags = static_cast<DWORD>(FD_PROGRESSUI | FD_UNICODE); | |
pin_ptr<const wchar_t> p0 = PtrToStringChars(filename0); | |
wcscpy_s(pfgd->fgd[0].cFileName, p0); | |
pfgd->fgd[1].dwFlags = static_cast<DWORD>(FD_PROGRESSUI | FD_UNICODE); | |
pin_ptr<const wchar_t> p1 = PtrToStringChars(filename1); | |
wcscpy_s(pfgd->fgd[1].cFileName, p1); | |
m_formatetc = gcnew array<ComTypes::FORMATETC> | |
{ | |
{ CF_FILEDESCRIPTOR, IntPtr::Zero, ComTypes::DVASPECT::DVASPECT_CONTENT, -1, ComTypes::TYMED::TYMED_HGLOBAL }, | |
{ CF_FILECONTENTS, IntPtr::Zero, ComTypes::DVASPECT::DVASPECT_CONTENT, 0, ComTypes::TYMED::TYMED_ISTREAM }, | |
{ CF_FILECONTENTS, IntPtr::Zero, ComTypes::DVASPECT::DVASPECT_CONTENT, 1, ComTypes::TYMED::TYMED_ISTREAM }, | |
}; | |
} | |
private: | |
array<BYTE>^ m_fileGroupDescriptorBuffer; | |
array<ComTypes::FORMATETC>^ m_formatetc; | |
int m_inOperationCount = 0; | |
}; | |
void OnMouseDown(Object^ sender, MouseEventArgs^ e) | |
{ | |
if ((e->Button & MouseButtons::Left) != MouseButtons()) | |
{ | |
Debug::WriteLine("Before DoDragDrop: " + Thread::CurrentThread->ManagedThreadId); | |
safe_cast<Control^>(sender)->DoDragDrop(gcnew AppDataObject, DragDropEffects::Copy); | |
Debug::WriteLine("After DoDragDrop: " + Thread::CurrentThread->ManagedThreadId); | |
} | |
} | |
[STAThread] | |
int main(array<System::String^> ^args) | |
{ | |
auto f = gcnew Form; | |
f->MouseDown += gcnew MouseEventHandler(OnMouseDown); | |
Application::Run(f); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment