Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@egtra
Created September 13, 2014 16:12
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/ad30f23d83502d845c2f to your computer and use it in GitHub Desktop.
Save egtra/ad30f23d83502d845c2f to your computer and use it in GitHub Desktop.
非同期ドロップ処理 + 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 <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 ATL::CComEnumImpl<IEnumFORMATETC, &__uuidof(IEnumFORMATETC), FORMATETC, ATL::_Copy<FORMATETC>>
, public IDataObjectAsyncCapability
{
BEGIN_COM_MAP(DataObject)
COM_INTERFACE_ENTRY(IDataObject)
COM_INTERFACE_ENTRY(IEnumFORMATETC)
COM_INTERFACE_ENTRY(IDataObjectAsyncCapability)
END_COM_MAP()
public:
DataObject();
HRESULT IDataObject_GetData(
_In_ FORMATETC* pformatetcIn,
_Out_ STGMEDIUM* pmedium)
{
if (pformatetcIn->cfFormat == CF_FILEDESCRIPTOR)
{
if (auto hGlobal = GlobalAlloc(GHND, sizeof m_fileGroupDescriptor))
{
if (auto p = GlobalLock(hGlobal))
{
memcpy(p, &m_fileGroupDescriptor, sizeof m_fileGroupDescriptor);
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)
{
static const BYTE content[] = { 'A', 'B', 'C' };
pmedium->tymed = TYMED_ISTREAM;
pmedium->pstm = SHCreateMemStream(content, sizeof content);
pmedium->pUnkForRelease = nullptr;
OutputDebugStringA(("GetData(CFSTR_FILECONTENTS): " + std::to_string(GetCurrentThreadId())).c_str());
return S_OK;
}
return E_NOTIMPL;
}
STDMETHOD(EnumFormatEtc)(
_In_ DWORD dwDirection,
_In_opt_ IEnumFORMATETC** ppenumFormatEtc) override
{
switch (dwDirection)
{
case DATADIR_GET:
if (ppenumFormatEtc == nullptr)
{
return E_POINTER;
}
else
{
*ppenumFormatEtc = this;
AddRef();
return S_OK;
}
case DATADIR_SET:
return E_INVALIDARG;
default:
return E_NOTIMPL;
}
}
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;
}
FILEGROUPDESCRIPTOR m_fileGroupDescriptor = {};
ATL::CComPtr<IDataAdviseHolder> m_spDataAdviseHolder;
private:
std::atomic<int> m_inOperationCount = 0;
static const FORMATETC s_formatetc[];
};
const FORMATETC DataObject::s_formatetc[] =
{
{ CF_FILEDESCRIPTOR, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
{ CF_FILECONTENTS, nullptr, DVASPECT_CONTENT, -1, TYMED_ISTREAM },
};
DataObject::DataObject()
{
m_fileGroupDescriptor.cItems = 1;
m_fileGroupDescriptor.fgd[0].dwFlags = static_cast<DWORD>(FD_PROGRESSUI | FD_UNICODE);
wcscpy_s(m_fileGroupDescriptor.fgd[0].cFileName, L"test.txt");
Init(
const_cast<FORMATETC*>(std::begin(s_formatetc)),
const_cast<FORMATETC*>(std::end(s_formatetc)),
nullptr);
}
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