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 <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> | |
{ | |
BEGIN_COM_MAP(DataObject) | |
COM_INTERFACE_ENTRY(IDataObject) | |
END_COM_MAP() | |
public: | |
DataObject(); | |
HRESULT IDataObject_GetData( | |
_In_ FORMATETC* pformatetcIn, | |
_Out_ STGMEDIUM* pmedium) | |
{ | |
if (pformatetcIn == nullptr) | |
return E_INVALIDARG; | |
if (pmedium == nullptr) | |
return E_POINTER; | |
if (pformatetcIn->dwAspect != DVASPECT_CONTENT) | |
return DV_E_DVASPECT; | |
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; | |
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; | |
return S_OK; | |
} | |
return DV_E_TYMED; | |
} | |
STDMETHOD(EnumFormatEtc)( | |
_In_ DWORD dwDirection, | |
__RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) override; | |
FILEGROUPDESCRIPTOR m_fileGroupDescriptor = {}; | |
ATL::CComPtr<IDataAdviseHolder> m_spDataAdviseHolder; | |
private: | |
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"); | |
} | |
STDMETHODIMP DataObject::EnumFormatEtc( | |
_In_ DWORD dwDirection, | |
__RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) | |
{ | |
switch (dwDirection) | |
{ | |
case DATADIR_GET: | |
return CreateFormatEnumerator( | |
ARRAYSIZE(s_formatetc), | |
const_cast<FORMATETC*>(s_formatetc), | |
ppenumFormatEtc); | |
case DATADIR_SET: | |
return E_NOTIMPL; | |
default: | |
return E_INVALIDARG; | |
} | |
} | |
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>(); | |
DoDragDrop(dataObject, this, DROPEFFECT_COPY, &effect); | |
} | |
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