Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egtra/8367b22f11b44ca2cedb to your computer and use it in GitHub Desktop.
Save egtra/8367b22f11b44ca2cedb to your computer and use it in GitHub Desktop.
非同期ドロップ処理フリースレッドマーシャラー + CFSTR_FILEDESCRIPTORで複数ファイル (Windows Forms + SHCreateMemStream)
#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