第 ⑩ 章 · 拖放处理器(IDropTarget)

让某类文件成为“拖放目标”——把文件拖到它上面松手时你的代码被调用;讲 IDropTarget 四方法、从 IDataObject 取 CF_HDROP、以及 shellex\DropHandler 注册。

作者 David更新于

拖放处理器让某类文件(或文件夹)成为拖放目标:当用户把一批文件拖到它的图标上松手时,你的代码被调用,拿到“被拖入了哪些文件”。资源管理器里那些“拖到某程序图标上用它打开”的行为,底层就是这个机制。配套代码:10-drop。

两个接口

  • IPersistFile —— Load 告诉你“用户拖到了哪个目标文件”(图标本身对应的文件路径)。
  • IDropTarget —— 标准 OLE 拖放目标接口,四个方法:
方法时机你要做的
DragEnter拖动进入图标检查数据格式,通过 *pdwEffect 表明能否接收(复制/移动/链接/拒绝)
DragOver在图标上移动持续更新 *pdwEffect(光标样式随之变化)
DragLeave离开图标清理 DragEnter 里的临时状态
Drop松手从 IDataObject 取出文件真正干活
DropHandler.cpp(节选)——松手时取文件列表
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,只是入口不同。

参考

本页目录