第 ⑩ 章 · 拖放处理器(IDropTarget)
让某类文件成为“拖放目标”——把文件拖到它上面松手时你的代码被调用;讲 IDropTarget 四方法、从 IDataObject 取 CF_HDROP、以及 shellex\DropHandler 注册。
作者 David更新于
拖放处理器让某类文件(或文件夹)成为拖放目标:当用户把一批文件拖到它的图标上松手时,你的代码被调用,拿到“被拖入了哪些文件”。资源管理器里那些“拖到某程序图标上用它打开”的行为,底层就是这个机制。配套代码:10-drop。
两个接口
IPersistFile——Load告诉你“用户拖到了哪个目标文件”(图标本身对应的文件路径)。IDropTarget—— 标准 OLE 拖放目标接口,四个方法:
| 方法 | 时机 | 你要做的 |
|---|---|---|
DragEnter | 拖动进入图标 | 检查数据格式,通过 *pdwEffect 表明能否接收(复制/移动/链接/拒绝) |
DragOver | 在图标上移动 | 持续更新 *pdwEffect(光标样式随之变化) |
DragLeave | 离开图标 | 清理 DragEnter 里的临时状态 |
Drop | 松手 | 从 IDataObject 取出文件真正干活 |
IFACEMETHODIMP DropHandler::Drop(IDataObject* pDataObj, DWORD, POINTL, DWORD* pdwEffect) {
if (pdwEffect) *pdwEffect = DROPEFFECT_COPY;
FORMATETC fe = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg;
if (SUCCEEDED(pDataObj->GetData(&fe, &stg))) {
HDROP hDrop = (HDROP)GlobalLock(stg.hGlobal);
UINT n = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0); // 先问总数
for (UINT i = 0; i < n; ++i) {
wchar_t f[MAX_PATH];
DragQueryFileW(hDrop, i, f, ARRAYSIZE(f)); // 再逐个取
/* …处理 f… */
}
GlobalUnlock(stg.hGlobal);
ReleaseStgMedium(&stg); // 一定要还回去
}
return S_OK;
}两个易错点:GetData 拿到的 STGMEDIUM 用完必须 ReleaseStgMedium,否则漏内存;DragQueryFileW 第二参传 0xFFFFFFFF 是“问总数”,传下标才是“取第几个”。
注册
拖放处理器是单个(不是列表),注册在 ProgID 的 shellex\DropHandler 下,默认值就是 CLSID:
HKCR\txtfile\shellex\DropHandler\ (默认) = {你的CLSID}拖放处理器与右键菜单处理器常常配合:很多“把文件拖到某图标上”的操作,和“右键某类文件 → 某命令”其实复用同一套处理逻辑。数据都来自 IDataObject,只是入口不同。