Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
非同期ドロップ処理フリースレッドマーシャラー + CFSTR_FILEDESCRIPTORで複数ファイル (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
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _WTL_NO_AUTOMATIC_NAMESPACE
#define NOMINMAX
#include <string.h>
#include <algorithm>
#include <atomic>
#include <string>
#include <vector>
#include <Windows.h>
#include <shlobj.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atltypes.h>
#include <atlwin.h>
#pragma warning(disable: 4996)
#include <atlapp.h>
#include <atlctrls.h>
#include <atlcrack.h>
#pragma comment(linker, "\"/manifestdependency:type='Win32' "\
"name='Microsoft.Windows.Common-Controls' version='6.0.0.0' "\
"processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
class Module : public ATL::CAtlExeModuleT<Module> {};
Module module;
static const auto CF_FILEDESCRIPTOR = static_cast<CLIPFORMAT>(::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
static const auto CF_FILECONTENTS = static_cast<CLIPFORMAT>(::RegisterClipboardFormat(CFSTR_FILECONTENTS));
template<typename T>
ATL::CComPtr<T> CreateComObject()
{
ATL::CComObject<T>* obj = nullptr;
auto hr = ATL::CComObject<T>::CreateInstance(&obj);
ATLENSURE_SUCCEEDED(hr);
return ATL::CComPtr<T>(obj);
}
class ATL_NO_VTABLE DataObject
: public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
, public ATL::IDataObjectImpl<DataObject>
, public IDataObjectAsyncCapability
{
BEGIN_COM_MAP(DataObject)
COM_INTERFACE_ENTRY(IDataObject)
COM_INTERFACE_ENTRY(IDataObjectAsyncCapability)
COM_INTERFACE_ENTRY_IID(__uuidof(IAgileObject), IDataObject)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_marshaler.p)
END_COM_MAP()
public:
DataObject();
HRESULT FinalConstruct()
{
return CoCreateFreeThreadedMarshaler(GetUnknown(), &m_marshaler);
}
HRESULT IDataObject_GetData(
_In_ FORMATETC* pformatetcIn,
_Out_ STGMEDIUM* pmedium)
{
if (pformatetcIn->dwAspect != DVASPECT_CONTENT)
{
return DV_E_DVASPECT;
}
if (pformatetcIn->cfFormat == CF_FILEDESCRIPTOR)
{
if (auto hGlobal = GlobalAlloc(GHND, m_fileGroupDescriptorBuffer.size()))
{
if (auto p = GlobalLock(hGlobal))
{
memcpy(p, m_fileGroupDescriptorBuffer.data(), m_fileGroupDescriptorBuffer.size());
GlobalUnlock(hGlobal);
pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = hGlobal;
pmedium->pUnkForRelease = nullptr;
OutputDebugStringA(("GetData(CFSTR_FILEDESCRIPTOR): " + std::to_string(GetCurrentThreadId())).c_str());
return S_OK;
}
}
return E_OUTOFMEMORY;
}
else if (pformatetcIn->cfFormat == CF_FILECONTENTS)
{
if (pformatetcIn->lindex == 0)
{
static const BYTE content[] = { 'R', 'i', 'n', 'g', 'o' };
pmedium->tymed = TYMED_ISTREAM;
pmedium->pstm = SHCreateMemStream(content, sizeof content);
pmedium->pUnkForRelease = nullptr;
OutputDebugStringA(("GetData(CFSTR_FILECONTENTS, 0): " + std::to_string(GetCurrentThreadId())).c_str());
return S_OK;
}
else if (pformatetcIn->lindex == 1)
{
static const BYTE content[] = { 'G', 'o', 'r', 'i', 'r', 'a' };
pmedium->tymed = TYMED_ISTREAM;
pmedium->pstm = SHCreateMemStream(content, sizeof content);
pmedium->pUnkForRelease = nullptr;
OutputDebugStringA(("GetData(CFSTR_FILECONTENTS, 1): " + std::to_string(GetCurrentThreadId())).c_str());
return S_OK;
}
else
{
return DV_E_LINDEX;
}
}
return DV_E_TYMED;
}
STDMETHOD(EnumFormatEtc)(
_In_ DWORD dwDirection,
__RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) override
{
switch (dwDirection)
{
case DATADIR_GET:
return CreateFormatEnumerator(m_formatetc.size(), m_formatetc.data(), ppenumFormatEtc);
case DATADIR_SET:
return E_NOTIMPL;
default:
return E_INVALIDARG;
}
}
STDMETHOD(SetAsyncMode)(BOOL fDoOpAsync) override
{
if (!fDoOpAsync)
{
return E_INVALIDARG;
}
return S_OK;
}
STDMETHOD(GetAsyncMode)(__RPC__out BOOL* pfIsOpAsync) override
{
if (pfIsOpAsync == nullptr)
{
return E_POINTER;
}
*pfIsOpAsync = TRUE;
return S_OK;
}
STDMETHOD(StartOperation)(__RPC__in_opt IBindCtx* /*pbcReserved*/)
{
OutputDebugStringA(("StartOperation: " + std::to_string(GetCurrentThreadId())).c_str());
m_inOperationCount++;
return S_OK;
}
STDMETHOD(InOperation)(__RPC__out BOOL *pfInAsyncOp) override
{
if (pfInAsyncOp == nullptr)
{
return E_POINTER;
}
*pfInAsyncOp = (m_inOperationCount > 0);
return S_OK;
}
STDMETHOD(EndOperation)(HRESULT /*hResult*/, __RPC__in_opt IBindCtx* /*pbcReserved*/, DWORD /*dwEffects*/) override
{
OutputDebugStringA(("EndOperation: " + std::to_string(GetCurrentThreadId())).c_str());
m_inOperationCount--;
return S_OK;
}
ATL::CComPtr<IDataAdviseHolder> m_spDataAdviseHolder;
private:
std::vector<BYTE> m_fileGroupDescriptorBuffer;
std::vector<FORMATETC> m_formatetc;
std::atomic<int> m_inOperationCount = 0;
ATL::CComPtr<IUnknown> m_marshaler;
};
DataObject::DataObject()
{
UINT numOfFile = 2;
m_fileGroupDescriptorBuffer.resize(offsetof(FILEGROUPDESCRIPTOR, fgd) + sizeof (FILEDESCRIPTOR) * numOfFile);
auto pfgd = reinterpret_cast<FILEGROUPDESCRIPTOR*>(m_fileGroupDescriptorBuffer.data());
pfgd->cItems = numOfFile;
pfgd->fgd[0].dwFlags = static_cast<DWORD>(FD_PROGRESSUI | FD_UNICODE);
wcscpy_s(pfgd->fgd[0].cFileName, L"ringo.txt");
pfgd->fgd[1].dwFlags = static_cast<DWORD>(FD_PROGRESSUI | FD_UNICODE);
wcscpy_s(pfgd->fgd[1].cFileName, L"gorira.txt");
m_formatetc =
{
{ CF_FILEDESCRIPTOR, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
{ CF_FILECONTENTS, nullptr, DVASPECT_CONTENT, 0, TYMED_ISTREAM },
{ CF_FILECONTENTS, nullptr, DVASPECT_CONTENT, 1, TYMED_ISTREAM },
};
}
class ATL_NO_VTABLE TestWindow
: public ATL::CWindowImpl<TestWindow>
, public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
, public IDropSource
{
public:
DECLARE_WND_CLASS(TEXT("Test Window Class"));
STDMETHOD(QueryContinueDrag)(
_In_ BOOL fEscapePressed,
_In_ DWORD grfKeyState) override
{
if (fEscapePressed)
{
return DRAGDROP_S_CANCEL;
}
if ((grfKeyState & MK_LBUTTON) == 0)
{
return DRAGDROP_S_DROP;
}
return S_OK;
}
STDMETHOD(GiveFeedback)(
_In_ DWORD dwEffect) override
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}
BEGIN_COM_MAP(TestWindow)
COM_INTERFACE_ENTRY(IDropSource)
END_COM_MAP()
private:
BEGIN_MSG_MAP(TestWindow)
MSG_WM_LBUTTONDOWN(OnLButtonDown)
MSG_WM_DESTROY(OnDestroy)
END_MSG_MAP()
void OnLButtonDown(UINT nFlags, POINT point)
{
DWORD effect;
auto dataObject = CreateComObject<DataObject>();
OutputDebugStringA(("Before DoDragDrop: " + std::to_string(GetCurrentThreadId())).c_str());
DoDragDrop(dataObject, this, DROPEFFECT_COPY, &effect);
OutputDebugStringA(("After DoDragDrop: " + std::to_string(GetCurrentThreadId())).c_str());
}
void OnDestroy()
{
PostQuitMessage(0);
}
};
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int cmdShow)
{
OleInitialize(nullptr);
InitCommonControls();
ATL::CComObjectGlobal<TestWindow> wnd;
if (!wnd.Create(nullptr, ATL::CWindow::rcDefault,
TEXT("Hello, world"), WS_OVERLAPPEDWINDOW))
{
return -1;
}
wnd.ShowWindow(cmdShow);
wnd.UpdateWindow();
WTL::CMessageLoop msgLoop;
auto ret = msgLoop.Run();
CoDisconnectObject(&wnd, 0);
OleUninitialize();
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.