# Windows Shell 扩展指南

> 面向 Windows 10/11 重写的 Shell 扩展开发指南。

- 作者：David（道雾轩）
- 最后更新：2026-08-03
- 原文：https://daiw.org/manual/windows-shell-ext
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

二十多年前，Michael Dunn 在 CodeProject 写了一套 *The Complete Idiot's Guide to Writing Shell Extensions*，我把它翻译成中文放在个人主页上，是很多人入门 Windows Shell 编程的第一站。但那套教程停在 2004 年——它讲的 `IColumnProvider`、`IExtractImage`、`IPersistFile` 初始化、乃至整套注册方式，在 Windows 10/11 上要么被**取代**、要么被**移除**、要么已经不是推荐做法。

这份指南**不是翻译，而是重写**：保留原系列由浅入深的例子脉络，但每一个都按 Win10/Win11 的现状与[最新官方文档](https://learn.microsoft.com/en-us/windows/win32/shell/shell-entry)重新构思。示例代码单独放在开源仓库 [davidapp/shell-extension-demos](https://github.com/davidapp/shell-extension-demos) 里维护（每个示例一个目录、自带 CMake 工程，clone 即可构建），正文只保留讲清原理所需的关键片段。

<Callout type="info">
  重点放在 **Windows 10 与 Windows 11**。凡涉及“老写法仍能用、但有更好的新做法”的地方，都会明确标注，并给出官方文档链接。
</Callout>

## 什么是 Shell 扩展

Windows 资源管理器（Explorer）本质上是一个可扩展的外壳。**Shell 扩展**就是你写的一个进程内 COM 组件（通常是一个 DLL），资源管理器在需要时把它加载进来，让你在特定环节“插一脚”：

- 用户右键一个文件 → 让你往快捷菜单里加命令；
- 资源管理器要画某个文件的缩略图 / 图标 → 让你提供自定义图像；
- 用户打开属性对话框、悬停看信息提示、在详细信息视图里排序分组 → 让你提供数据；
- 用户拖放、预览文件 → 让你接管。

关键区别：注册表也能做一些定制（比如给某类文件配一个固定图标），但**注册表只能“一刀切”**——同一类型的所有文件长一个样。Shell 扩展则能**逐文件**决定行为（这个文本文件显示这个图标、那个显示那个）。这正是[官方文档](https://learn.microsoft.com/en-us/windows/win32/shell/handlers)里区分“注册表定制”与“扩展处理器（handler）”的分界线。

## 二十年后：到底变了什么

如果你读过老版指南，先看这张对照表——它决定了这份新指南每一章要怎么写：

| 老指南里的例子 | 当年的接口 | Windows 10/11 现状 |
| --- | --- | --- |
| 文本文件右键菜单 | `IContextMenu` | 仍可用；但 **Win11 新版右键菜单**需要 `IExplorerCommand` + MSIX 打包，未打包的处理器只落到「显示更多选项」旧菜单里 |
| 多文件右键 + `regsvr32` 注册 | `IContextMenu` | 逻辑不变；注册未打包走注册表、打包走包清单 |
| 弹出信息提示（用 MFC） | `IQueryInfo` | 仍可用；更推荐走**属性系统**（`prop:` 字符串或 `IPropertyStore`） |
| 拖放建硬链接 | `IDropTarget` | 仍可用 |
| 文件属性页 | `IShellPropSheetExt` | 仍可用 |
| 「复制到 / 发送到」右键 | `IContextMenu`（右键拖放） | 仍可用；配套的 **DDE 已弃用**，改用 `IDropTarget` 激活 |
| 文件夹背景菜单 + BMP 缩略图 | `IExtractImage` | 缩略图改用 **`IThumbnailProvider`**（Vista 起推荐） |
| 详细信息列（列处理器，读 MP3 标签） | `IColumnProvider` | **Vista 起已移除**；改用**属性处理器 `IPropertyStore`** + 属性 schema |
| 按文件大小显示不同图标 | `IExtractIcon` | 仍可用 |

除此之外，还有两处“地基级”的变化贯穿所有处理器：

- **初始化接口**：老代码用 `IPersistFile` / `IShellExtInit` 拿到文件名。Vista 起这些被 [`IInitializeWithItem`](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-iinitializewithitem)、[`IInitializeWithStream`](https://learn.microsoft.com/en-us/windows/win32/api/propsys/nn-propsys-iinitializewithstream)、[`IInitializeWithFile`](https://learn.microsoft.com/en-us/windows/win32/api/propsys/nn-propsys-iinitializewithfile) 取代（图标处理器现在也不再强制 `IPersistFile`）。
- **属性系统统一化**：详细信息列、信息提示、属性页里的字段、搜索与分组，如今都可以由**一个属性处理器**（`IPropertyStore`）统一供数据，不再各写各的。老指南里“列处理器”这一章，在新版里会整体并入“属性处理器”。

<Callout type="warn">
  **`IColumnProvider`（列处理器）在 Windows Vista 就被移除了**，别再照老教程写。要往详细信息视图加列，正确做法是实现属性处理器并注册属性 schema——本指南有专门一章。
</Callout>

## 你需要的工具

- **Windows 11**（或 Windows 10）+ 一台能随便重启资源管理器、装卸扩展的开发机（强烈建议用虚拟机，扩展写崩了会拖垮 Explorer）。
- **Visual Studio 2022**，含「使用 C++ 的桌面开发」与「通用 Windows 平台开发 / MSIX 打包工具」工作负载。
- **C++17**、Windows SDK（较新版本）。
- **[WIL（Windows Implementation Library）](https://github.com/microsoft/wil)**：微软官方的现代 C++ 头文件库，`wil::com_ptr`、RAII 句柄、错误处理宏能让 COM 代码干净很多。老教程里的 ATL 仍可用，但新代码更推荐 WIL（可与 ATL 并存）。
- 调试利器：`Process Monitor`、`DebugView`，以及资源管理器的独立进程选项（方便挂调试器、崩了不连累桌面）。

<Callout type="info">
  微软把大量**现成、可编译**的扩展范例放在 [Windows-classic-samples](https://github.com/microsoft/Windows-classic-samples) 仓库（搜 `ThumbnailProvider`、`PreviewHandler`、`ExplorerCommandVerb`、`PropertyHandler` 等目录）。本指南的示例仓库会以它们为骨架，去掉过时写法、补齐 Win11 打包。
</Callout>

## 先打好地基：COM 与 DLL

绝大多数 Shell 扩展是**进程内（in-process）COM 对象**，打包成 DLL。要成为一个合格的扩展，它至少要：

- 有一个唯一的 **GUID（CLSID）**；
- 实现 [`IUnknown`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown) 与一个**类工厂**（class factory）；
- 从 DLL 导出三个标准函数：[`DllGetClassObject`](https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-dllgetclassobject)（暴露类工厂）、[`DllCanUnloadNow`](https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-dllcanunloadnow)（告诉系统没人用了可以卸载）、`DllMain`；
- 实现对应处理器的接口（`IContextMenu`、`IThumbnailProvider`……）以及现代初始化接口（`IInitializeWithItem` 等）。

线程模型统一用 **Apartment**。这些“样板”在每个扩展里几乎一样，示例仓库会把它抽成可复用的基类，正文则聚焦每种处理器**独有**的接口。

## 注册：注册表 与 MSIX 两条路

同一个处理器，在 Win10/11 上有两种“让系统认识它”的方式，本指南两条都会讲：

1. **注册表（未打包）**：经典做法。在 `HKEY_CLASSES_ROOT` 下写 `CLSID\{GUID}\InProcServer32` 指向 DLL，再到文件类型的 `ShellEx\<处理器子键>` 下挂上 GUID。改动后要调用 [`SHChangeNotify(SHCNE_ASSOCCHANGED, …)`](https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shchangenotify) 通知系统。适合快速开发、内部工具。
2. **MSIX / 稀疏包（packaged）**：现代做法，也是**进入 Windows 11 新版右键菜单的唯一途径**。用包清单（`appxmanifest`）里的 `com:Extension` + `desktop4/desktop5:Extension` 声明扩展，系统按包来加载、更新、卸载，干净且可上架。缩略图、预览、右键命令等都能这样注册。

<Callout type="warn">
  进程内扩展是被**加载进资源管理器（或其它宿主进程）**里跑的——一旦你的代码崩了或死锁，轻则该功能失灵，重则整个桌面卡死。所以每个扩展都要：严格判空与错误处理、绝不在 UI 线程做耗时/阻塞操作、务必在虚拟机里测。可能的话优先用**进程外代理宿主**（如预览处理器天然跑在 `prevhost.exe` 里）。
</Callout>

## 本指南结构

按“一次一个例子”推进，每一章聚焦一种处理器，给出可运行的最小示例 + Win10/11 注意点 + 官方文档链接：

1. **右键菜单扩展（`IContextMenu`）** —— 第一个 in-proc DLL 扩展，把 COM/DLL 地基走通
2. **现代命令模型 `IExplorerCommand`** —— 更简单的命令写法、级联子菜单、动态启用/隐藏
3. **Windows 11 新版右键菜单** —— 用 MSIX / 稀疏包把命令送进主菜单
4. **缩略图处理器 `IThumbnailProvider`** —— 取代 `IExtractImage`
5. **图标处理器 `IExtractIcon` 与图标叠加 `IShellIconOverlayIdentifier`**
6. **属性处理器 `IPropertyStore` 与属性 schema** —— 详细信息列 / 分组 / 信息提示的统一数据源（取代列处理器）
7. **信息提示 `IQueryInfo`**
8. **属性表页 `IShellPropSheetExt`**
9. **预览处理器 `IPreviewHandler`**
10. **拖放处理器 `IDropTarget`**
11. **打包、部署、调试与旧代码迁移**

> 章节顺序可能随写作微调；每章配套的完整、可编译示例见开源仓库 [davidapp/shell-extension-demos](https://github.com/davidapp/shell-extension-demos)。第 ① 章的 [`01-context-menu`](https://github.com/davidapp/shell-extension-demos/tree/main/01-context-menu) 已可直接构建运行。

## 参考

- [Windows Shell 文档入口](https://learn.microsoft.com/en-us/windows/win32/shell/shell-entry)
- [创建 Shell 扩展处理器](https://learn.microsoft.com/en-us/windows/win32/shell/handlers)（各处理器总览、注册机制）
- [创建快捷（右键）菜单处理器](https://learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers)
- [Windows-classic-samples 示例集](https://github.com/microsoft/Windows-classic-samples)
- [WIL（Windows Implementation Library）](https://github.com/microsoft/wil)

## 本专栏篇目

- [概述：二十年后的 Shell 扩展](https://daiw.org/manual/windows-shell-ext.md): 面向 Windows 10/11 重写的 Shell 扩展开发指南。

### 右键菜单

- [第 ① 章 · 右键菜单扩展（IContextMenu）](https://daiw.org/manual/windows-shell-ext/context-menu.md): 用一个最小的进程内 COM DLL 走通 Shell 扩展的地基——IShellExtInit 拿文件、IContextMenu 加菜单并执行、注册表挂载，以及 Win11 的「显示更多选项」。
- [第 ② 章 · 现代命令模型（IExplorerCommand）](https://daiw.org/manual/windows-shell-ext/explorer-command.md): 用 IExplorerCommand 重写右键命令——标题/图标/状态/执行拆成独立方法，选中项走 IShellItemArray，注册改用 ExplorerCommandHandler；它也是进入 Win11 主菜单的实现基础。
- [第 ③ 章 · 进入 Windows 11 主右键菜单（MSIX / 稀疏包）](https://daiw.org/manual/windows-shell-ext/win11-menu.md): 不改一行处理器代码，用稀疏包（sparse package）给第②章的 IExplorerCommand 赋予“包标识”，把命令送进 Win11 的新版主右键菜单；含 AppxManifest、嵌入标识清单、自签名与注册的完整流程。

### 文件视觉

- [第 ④ 章 · 缩略图处理器（IThumbnailProvider）](https://daiw.org/manual/windows-shell-ext/thumbnail.md): 用 IThumbnailProvider 为文件生成自定义缩略图，取代已过时的 IExtractImage；讲 IInitializeWithStream 拿内容、GDI 画 32bpp 位图、WTS_ALPHATYPE 与注册。
- [第 ⑤ 章 · 图标处理器与图标叠加](https://daiw.org/manual/windows-shell-ext/icon.md): 用 IExtractIcon 让同类文件逐个显示不同图标（GIL_PERINSTANCE 是关键），并讲 IShellIconOverlayIdentifier 图标叠加与它著名的“15 个槽位”限制。

### 属性与预览

- [第 ⑥ 章 · 属性处理器（IPropertyStore）](https://daiw.org/manual/windows-shell-ext/property-handler.md): 用一个 IPropertyStore 统一供数据给详细信息列、信息提示、分组和搜索——这是老“列处理器 IColumnProvider（Vista 已移除）”的现代替代；讲 PROPERTYKEY、系统属性与自定义 .propdesc schema。
- [第 ⑦ 章 · 信息提示处理器（IQueryInfo）](https://daiw.org/manual/windows-shell-ext/infotip.md): 用 IQueryInfo 定制鼠标悬停文件时的提示文字；以及不写代码、用 InfoTip 属性列表字符串的更省事替代。
- [第 ⑧ 章 · 属性表处理器（IShellPropSheetExt）](https://daiw.org/manual/windows-shell-ext/property-sheet.md): 往文件「属性」对话框里加一个自定义标签页——IShellExtInit 拿文件、AddPages 用对话框模板造页、PSPCB 回调管生命周期。
- [第 ⑨ 章 · 预览处理器（IPreviewHandler）](https://daiw.org/manual/windows-shell-ext/preview.md): 在资源管理器预览窗格里渲染文件——它跑在独立宿主 prevhost.exe 里（靠 AppID 隔离）；讲 SetWindow/DoPreview/Unload、IObjectWithSite 与三处注册。

### 拖放与图标叠加

- [第 ⑩ 章 · 拖放处理器（IDropTarget）](https://daiw.org/manual/windows-shell-ext/drop-handler.md): 让某类文件成为“拖放目标”——把文件拖到它上面松手时你的代码被调用；讲 IDropTarget 四方法、从 IDataObject 取 CF_HDROP、以及 shellex\DropHandler 注册。
- [第 ⑪ 章 · 图标叠加处理器（IShellIconOverlayIdentifier）](https://daiw.org/manual/windows-shell-ext/icon-overlay.md): 给文件图标左下角叠一个小角标——OneDrive/Dropbox/TortoiseGit 的同步状态标就是它；讲 IsMemberOf 判定、15 个槽位的硬上限与抢位技巧、以及“必须快”的铁律。

### 收尾

- [第 ⑫ 章 · 调试、部署与现代化迁移](https://daiw.org/manual/windows-shell-ext/migration.md): 收尾章——如何调试跑在 explorer/dllhost/prevhost 里的扩展、per-user 与 per-machine 注册、MSIX/稀疏包部署，以及一张“旧接口 → 新接口”的迁移对照表。
