Skip to content

Instantly share code, notes, and snippets.

@59de44955ebd
Last active July 23, 2026 11:04
Show Gist options
  • Select an option

  • Save 59de44955ebd/548f4cd598dd0142a0e13da6773b6cfa to your computer and use it in GitHub Desktop.

Select an option

Save 59de44955ebd/548f4cd598dd0142a0e13da6773b6cfa to your computer and use it in GitHub Desktop.
Minimal virtual file drop (or put into clipboard) implementation for Windows
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
#include <vector>
#define WINDOW_CLASS L"VirtualFileDrop"
HINSTANCE g_hinst;
class CDataObject : public IDataObject
{
public:
// *** IUnknown ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// *** IDataObject ***
STDMETHODIMP GetData(FORMATETC * pformatetcIn, STGMEDIUM * pmedium);
STDMETHODIMP QueryGetData(FORMATETC * pformatetc);
STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC ** ppenumFormatEtc);
// Not implemented
STDMETHODIMP GetDataHere(FORMATETC * pformatetcIn, STGMEDIUM * pmedium) { return E_NOTIMPL; }
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC * pformatectIn, FORMATETC * pformatetcOut) { return E_NOTIMPL; }
STDMETHODIMP SetData(FORMATETC * pformatetcIn, STGMEDIUM * pmedium, BOOL fRelease) { return E_NOTIMPL; }
STDMETHODIMP DAdvise(FORMATETC * pformatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; }
STDMETHODIMP DUnadvise(DWORD dwConnection) { return OLE_E_ADVISENOTSUPPORTED; }
STDMETHODIMP EnumDAdvise(IEnumSTATDATA ** ppenumFormatEtc) { return OLE_E_ADVISENOTSUPPORTED; }
CDataObject(const wchar_t * filename, const std::vector<uint8_t>& data) : m_cRef(1), m_filename(filename), m_data(data)
{
m_formatFileDescriptorW = RegisterClipboardFormatW(CFSTR_FILEDESCRIPTOR);
m_formatFileContents = RegisterClipboardFormatW(CFSTR_FILECONTENTS);
}
private:
ULONG m_cRef;
UINT m_formatFileDescriptorW;
UINT m_formatFileContents;
const wchar_t * m_filename;
const std::vector<uint8_t> m_data;
};
HRESULT CDataObject::QueryInterface(REFIID riid, void **ppv)
{
IUnknown *punk = NULL;
if (riid == IID_IUnknown)
punk = static_cast<IUnknown*>(this);
else if (riid == IID_IDataObject)
punk = static_cast<IDataObject*>(this);
*ppv = punk;
if (punk)
{
punk->AddRef();
return S_OK;
}
else
return E_NOINTERFACE;
}
ULONG CDataObject::AddRef()
{
return ++m_cRef;
}
ULONG CDataObject::Release()
{
ULONG cRef = --m_cRef;
if (cRef == 0)
delete this;
return cRef;
}
HRESULT CDataObject::GetData(FORMATETC * pformatetcIn, STGMEDIUM * pmedium)
{
if (pformatetcIn->cfFormat == m_formatFileDescriptorW)
{
FILEGROUPDESCRIPTORW fgd = {};
fgd.cItems = 1;
wcscpy_s(fgd.fgd[0].cFileName, m_filename);
fgd.fgd[0].nFileSizeLow = (DWORD)m_data.size();
fgd.fgd[0].dwFlags = FD_FILESIZE | FD_UNICODE;
pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(FILEGROUPDESCRIPTORW));
HGLOBAL pcontents = GlobalLock(pmedium->hGlobal);
memcpy(pcontents, &fgd, sizeof(FILEGROUPDESCRIPTORW));
GlobalUnlock(pmedium->hGlobal);
return S_OK;
}
else if (pformatetcIn->cfFormat == m_formatFileContents)
{
pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, m_data.size());
HGLOBAL pcontents = GlobalLock(pmedium->hGlobal);
memcpy(pcontents, m_data.data(), m_data.size());
GlobalUnlock(pmedium->hGlobal);
return S_OK;
}
return DV_E_FORMATETC;
}
HRESULT CDataObject::QueryGetData(FORMATETC * pformatetc)
{
if (pformatetc->cfFormat == m_formatFileDescriptorW || pformatetc->cfFormat == m_formatFileContents)
return S_OK;
return S_FALSE;
}
HRESULT CDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC ** ppenumFormatEtc)
{
if (dwDirection == DATADIR_GET)
{
FORMATETC rgfe[2] = {};
rgfe[0].cfFormat = m_formatFileDescriptorW;
rgfe[0].tymed = TYMED_HGLOBAL;
rgfe[0].lindex = -1;
rgfe[0].dwAspect = DVASPECT_CONTENT;
rgfe[1].cfFormat = m_formatFileContents;
rgfe[1].tymed = TYMED_HGLOBAL;
rgfe[1].lindex = 0;
rgfe[1].dwAspect = DVASPECT_CONTENT;
return SHCreateStdEnumFmtEtc(2, rgfe, ppenumFormatEtc);
}
return S_FALSE;
}
void PutVirtualFileIntoClipboard()
{
// For binary data containing null bytes:
//const std::vector<uint8_t> data = { 'A', 'B', '\0', 'D', 'E' };
const std::string str = "[InternetShortcut]\r\nURL=https://www.google.com/\r\n";
const std::vector<uint8_t> data(str.begin(), str.end());
IDataObject * dataObject = new CDataObject(L"Google.url", data);
if (dataObject)
{
OleSetClipboard(dataObject);
dataObject->Release();
}
}
void StartVirtualFileDragDropOperation()
{
const std::string str = "[InternetShortcut]\r\nURL=https://www.google.com/\r\n";
const std::vector<uint8_t> data(str.begin(), str.end());
IDataObject * dataObject = new CDataObject(L"Google.url", data);
if (dataObject)
{
DWORD dwEffect;
SHDoDragDrop(NULL, dataObject, NULL, DROPEFFECT_COPY, &dwEffect);
dataObject->Release();
}
}
void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
//PutVirtualFileIntoClipboard();
StartVirtualFileDragDropOperation();
}
void OnDestroy(HWND hwnd)
{
PostQuitMessage(0);
}
/*
* Window procedure
*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
}
return DefWindowProcW(hwnd, uiMsg, wParam, lParam);
}
BOOL InitApp(void)
{
WNDCLASSW wc;
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hinst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = WINDOW_CLASS;
if (!RegisterClassW(&wc))
return FALSE;
return TRUE;
}
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nShowCmd)
{
MSG msg;
HWND hwnd;
g_hinst = hinst;
if (!InitApp())
return 0;
if (SUCCEEDED(OleInitialize(NULL)))
{
hwnd = CreateWindowW(
WINDOW_CLASS, /* Class Name */
L"VirtualFileDrop", /* Title */
WS_OVERLAPPEDWINDOW, /* Style */
CW_USEDEFAULT, CW_USEDEFAULT, /* Position */
320, 240, /* Size */
NULL, /* Parent */
NULL, /* No menu */
hinst, /* Instance */
0
);
ShowWindow(hwnd, nShowCmd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
OleUninitialize();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment