Created
May 18, 2015 19:36
-
-
Save egtra/57270aefac4022d3088e to your computer and use it in GitHub Desktop.
非同期ドロップ処理フリースレッドマーシャラー + CFSTR_FILEDESCRIPTORで複数ファイル (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 | |
#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