2025-11-28 00:35:46 +09:00

273 lines
8.3 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#pragma once
#include <strsafe.h>
#include <shlwapi.h>
#include <shlobj.h>
// declare a static CLIPFORMAT and pass that that by ref as the first param
__inline CLIPFORMAT GetClipboardFormat(CLIPFORMAT *pcf, PCWSTR pszForamt)
{
if (*pcf == 0)
{
*pcf = (CLIPFORMAT)RegisterClipboardFormat(pszForamt);
}
return *pcf;
}
__inline HRESULT SetBlob(IDataObject *pdtobj, CLIPFORMAT cf, const void *pvBlob, UINT cbBlob)
{
void *pv = GlobalAlloc(GPTR, cbBlob);
HRESULT hr = pv ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
CopyMemory(pv, pvBlob, cbBlob);
FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
// The STGMEDIUM structure is used to define how to handle a global memory transfer.
// This structure includes a flag, tymed, which indicates the medium
// to be used, and a union comprising pointers and a handle for getting whichever
// medium is specified in tymed.
STGMEDIUM medium = {};
medium.tymed = TYMED_HGLOBAL;
medium.hGlobal = pv;
hr = pdtobj->SetData(&fmte, &medium, TRUE);
if (FAILED(hr))
{
GlobalFree(pv);
}
}
return hr;
}
__inline void SetDropTip(IDataObject *pdtobj, DROPIMAGETYPE type, PCWSTR pszMsg, PCWSTR pszInsert)
{
DROPDESCRIPTION dd = { type };
StringCchCopyW(dd.szMessage, ARRAYSIZE(dd.szMessage), pszMsg);
StringCchCopyW(dd.szInsert, ARRAYSIZE(dd.szInsert), pszInsert ? pszInsert : L"");
static CLIPFORMAT s_cfDropDescription = 0;
SetBlob(pdtobj, GetClipboardFormat(&s_cfDropDescription, CFSTR_DROPDESCRIPTION), &dd, sizeof(dd));
}
__inline void ClearDropTip(IDataObject *pdtobj)
{
SetDropTip(pdtobj, DROPIMAGE_INVALID, L"", NULL);
}
// helper to convert a data object with HIDA format or folder into a shell item
// note: if the data object contains more than one item this function will fail
// if you want to operate on the full selection use SHCreateShellItemArrayFromDataObject
HRESULT CreateItemFromObject(IUnknown *punk, REFIID riid, void **ppv)
{
*ppv = NULL;
PIDLIST_ABSOLUTE pidl;
HRESULT hr = SHGetIDListFromObject(punk, &pidl);
if (SUCCEEDED(hr))
{
hr = SHCreateItemFromIDList(pidl, riid, ppv);
ILFree(pidl);
}
else
{
// perhaps the input is from IE and if so we can construct an item from the URL
IDataObject *pdo;
hr = punk->QueryInterface(IID_PPV_ARGS(&pdo));
if (SUCCEEDED(hr))
{
static CLIPFORMAT g_cfURL = 0;
FORMATETC fmte = {GetClipboardFormat(&g_cfURL, CFSTR_SHELLURL), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
hr = pdo->GetData(&fmte, &medium);
if (SUCCEEDED(hr))
{
PCSTR pszURL = (PCSTR)GlobalLock(medium.hGlobal);
if (pszURL)
{
WCHAR szURL[2048];
SHAnsiToUnicode(pszURL, szURL, ARRAYSIZE(szURL));
hr = SHCreateItemFromParsingName(szURL, NULL, riid, ppv);
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
pdo->Release();
}
}
return hr;
}
// encapsulation of the shell drag drop presentation and Drop handling functionality
// this provides the following features 1) drag images, 2) drop tips,
// 3) ints OLE and registers drop target, 4) conversion of the data object item into shell items
//
// to use this
// 1) have the object that represents the main window of your app derive
// from CDragDropHelper exposing it as public.
// "class CAppClass : public CDragDropHelper"
// 2) add IDropTarget to the QueryInterface() implementation of your class
// that is a COM object itself
// 3) in your WM_INITDIALOG handling call
// InitializeDragDropHelper(_hdlg, DROPIMAGE_LINK, NULL) passing
// the dialog window and drop tip template and type
// 4) in your WM_DESTROY handler add a call to TerminateDragDropHelper(). note not
// doing this will lead to a leak of your object so this is important
// 5) add the delaration and implementation of OnDrop() to your class, this
// gets called when the drop happens
class CDragDropHelper : public IDropTarget
{
public:
CDragDropHelper() : _pdth(NULL), _pdtobj(NULL), _hrOleInit(OleInitialize(0)), _hwndRegistered(NULL), _dropImageType(DROPIMAGE_LABEL), _pszDropTipTemplate(NULL)
{
CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&_pdth));
}
~CDragDropHelper()
{
SafeRelease(&_pdth);
if (SUCCEEDED(_hrOleInit))
{
OleUninitialize();
}
}
void SetDropTipTemplate(PCWSTR pszDropTipTemplate)
{
_pszDropTipTemplate = pszDropTipTemplate;
}
void InitializeDragDropHelper(HWND hwnd, DROPIMAGETYPE dropImageType, PCWSTR pszDropTipTemplate)
{
if (SUCCEEDED(_hrOleInit))
{
_dropImageType = dropImageType;
_pszDropTipTemplate = pszDropTipTemplate;
if (SUCCEEDED(RegisterDragDrop(hwnd, this)))
{
_hwndRegistered = hwnd;
}
}
}
void TerminateDragDropHelper()
{
if (_hwndRegistered)
{
RevokeDragDrop(_hwndRegistered);
_hwndRegistered = NULL;
}
}
HRESULT GetDragDropHelper(REFIID riid, void **ppv)
{
*ppv = NULL;
return _pdth ? _pdth->QueryInterface(riid, ppv) : E_NOINTERFACE;
}
// direct access to the data object, if you don't want to use the shell item array
IDataObject *GetDataObject()
{
return _pdtobj;
}
// IDropTarget
IFACEMETHODIMP DragEnter(IDataObject *pdtobj, DWORD /* grfKeyState */, POINTL pt, DWORD *pdwEffect)
{
// leave *pdwEffect unchanged, we support all operations
if (_pdth)
{
POINT ptT = { pt.x, pt.y };
_pdth->DragEnter(_hwndRegistered, pdtobj, &ptT, *pdwEffect);
}
SetInterface(&_pdtobj, pdtobj);
IShellItem *psi;
HRESULT hr = CreateItemFromObject(pdtobj, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName);
if (SUCCEEDED(hr))
{
SetDropTip(pdtobj, _dropImageType, _pszDropTipTemplate ? _pszDropTipTemplate : L"%1", pszName);
CoTaskMemFree(pszName);
}
psi->Release();
}
return S_OK;
}
IFACEMETHODIMP DragOver(DWORD /* grfKeyState */, POINTL pt, DWORD *pdwEffect)
{
// leave *pdwEffect unchanged, we support all operations
if (_pdth)
{
POINT ptT = { pt.x, pt.y };
_pdth->DragOver(&ptT, *pdwEffect);
}
return S_OK;
}
IFACEMETHODIMP DragLeave()
{
if (_pdth)
{
_pdth->DragLeave();
}
ClearDropTip(_pdtobj);
SafeRelease(&_pdtobj);
return S_OK;
}
IFACEMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
if (_pdth)
{
POINT ptT = { pt.x, pt.y };
_pdth->Drop(pdtobj, &ptT, *pdwEffect);
}
IShellItemArray *psia;
HRESULT hr = SHCreateShellItemArrayFromDataObject(_pdtobj, IID_PPV_ARGS(&psia));
if (SUCCEEDED(hr))
{
OnDrop(psia, grfKeyState);
psia->Release();
}
else
{
OnDropError(_pdtobj);
}
return S_OK;
}
private:
// client provides
virtual HRESULT OnDrop(IShellItemArray *psia, DWORD grfKeyState) = 0;
virtual HRESULT OnDropError(IDataObject * /* pdtobj */)
{
return S_OK;
}
IDropTargetHelper *_pdth;
IDataObject *_pdtobj;
DROPIMAGETYPE _dropImageType;
PCWSTR _pszDropTipTemplate;
HWND _hwndRegistered;
HRESULT _hrOleInit;
};