# 第 ⑩ 章 · 拖放处理器（IDropTarget）

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

- 作者：David（道雾轩）
- 专栏：Windows Shell 扩展指南（https://daiw.org/manual/windows-shell-ext.md）
- 最后更新：2026-07-12
- 原文：https://daiw.org/manual/windows-shell-ext/drop-handler
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

**拖放处理器**让某类文件（或文件夹）成为**拖放目标**：当用户把一批文件拖到它的图标上松手时，你的代码被调用，拿到“被拖入了哪些文件”。资源管理器里那些“拖到某程序图标上用它打开”的行为，底层就是这个机制。配套代码：[`10-drop`](https://github.com/davidapp/shell-extension-demos/tree/main/10-drop)。

## 两个接口

- **`IPersistFile`** —— `Load` 告诉你“用户拖到了哪个目标文件”（图标本身对应的文件路径）。
- **`IDropTarget`** —— 标准 OLE 拖放目标接口，四个方法：

| 方法 | 时机 | 你要做的 |
| --- | --- | --- |
| `DragEnter` | 拖动进入图标 | 检查数据格式，通过 `*pdwEffect` 表明能否接收（复制/移动/链接/拒绝） |
| `DragOver` | 在图标上移动 | 持续更新 `*pdwEffect`（光标样式随之变化） |
| `DragLeave` | 离开图标 | 清理 `DragEnter` 里的临时状态 |
| `Drop` | 松手 | 从 `IDataObject` 取出文件真正干活 |

```cpp title="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;
}
```

<Callout type="warn">
  两个易错点：`GetData` 拿到的 `STGMEDIUM` 用完**必须 `ReleaseStgMedium`**，否则漏内存；`DragQueryFileW` 第二参传 `0xFFFFFFFF` 是“问总数”，传下标才是“取第几个”。
</Callout>

## 注册

拖放处理器是**单个**（不是列表），注册在 ProgID 的 `shellex\DropHandler` 下，默认值就是 CLSID：

```
HKCR\txtfile\shellex\DropHandler\  (默认) = {你的CLSID}
```

<Callout type="info">
  拖放处理器与[右键菜单处理器](https://daiw.org/manual/windows-shell-ext/context-menu)常常配合：很多“把文件拖到某图标上”的操作，和“右键某类文件 → 某命令”其实复用同一套处理逻辑。数据都来自 `IDataObject`，只是入口不同。
</Callout>

## 参考

- [IDropTarget](https://learn.microsoft.com/zh-cn/windows/win32/api/oleidl/nn-oleidl-idroptarget) · [Drop handlers](https://learn.microsoft.com/zh-cn/windows/win32/shell/drophandlers)
- [传输 Shell 对象（拖放与剪贴板）](https://learn.microsoft.com/zh-cn/windows/win32/shell/datascenarios)
