# 第 ⑫ 章 · 调试、部署与现代化迁移

> 收尾章——如何调试跑在 explorer/dllhost/prevhost 里的扩展、per-user 与 per-machine 注册、MSIX/稀疏包部署，以及一张“旧接口 → 新接口”的迁移对照表。

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

前面每章一个扩展点。这一章把散落的工程问题收一下口：**怎么调试、怎么干净地注册、怎么部署**，以及——本指南从头到尾的主线——**把 20 年前的老写法迁移到 Win10/11 的新接口**。

## 一、调试：你的 DLL 跑在别人的进程里

Shell 扩展是**进程内 COM 组件**，被宿主进程加载。调试的第一步是搞清楚“宿主是谁”：

| 扩展类型 | 宿主进程 | 怎么调 |
| --- | --- | --- |
| 右键菜单 / 图标 / InfoTip / 属性页 | `explorer.exe` | 附加到 explorer；改完 DLL 前先 `taskkill /f /im explorer.exe` 再重启 |
| 缩略图 / 属性处理器 | `dllhost.exe`(COM Surrogate) | 附加到对应的 dllhost 实例 |
| 预览处理器 | `prevhost.exe` | 独立进程，直接附加最省心 |

<Callout type="warn">
  最常见的坑：**explorer 把你的 DLL 锁住了**，`cmake --build` 覆盖不了旧文件。解决办法是每次重建前重启 explorer，或者开发期给 CLSID 挂 `AppID` + `DllSurrogate`，让扩展跑进独立的 `dllhost` 里——崩了也不拖垮桌面(这正是[缩略图](https://daiw.org/manual/windows-shell-ext/thumbnail)、[预览](https://daiw.org/manual/windows-shell-ext/preview)默认隔离的原因)。
</Callout>

调试期开隔离，给 CLSID 加一个空的 `AppID` 值即可让系统用 `dllhost` 托管：

```
HKCR\CLSID\{你的CLSID}\  AppID = {你的CLSID}
HKCR\AppID\{你的CLSID}\  DllSurrogate = ""   （空字符串 = 用默认代理 dllhost）
```

## 二、注册：per-user 还是 per-machine

`regsvr32` 默认写 `HKEY_CLASSES_ROOT`，需要管理员。**现代推荐 per-user 注册**——写 `HKCU\Software\Classes`，免管理员、卸载干净、不影响其他用户：

```
HKCU\Software\Classes\CLSID\{CLSID}\...           ← 组件本体
HKCU\Software\Classes\.ext\shellex\...            ← 关联
```

本指南的示例为求简洁写的是 `HKCR`(= 管理员 per-machine)。真实产品优先 per-user，除非扩展确实要对所有用户生效。

<Callout type="info">
  在非默认(non-per-user)场景，企业环境可能启用“仅允许已批准的扩展”策略(`HKLM\...\Shell Extensions\Approved`)。个人机通常不受影响，但企业分发时要留意。
</Callout>

## 三、部署：从 regsvr32 到 MSIX

`regsvr32` + 写注册表是经典做法，但 Win10/11 的现代分发是 **MSIX / 稀疏包(sparse package)**:

- **声明式**：扩展点写在 `AppxManifest.xml` 里(见 [Win11 菜单章](https://daiw.org/manual/windows-shell-ext/win11-menu))，系统按清单注册，不用手写注册表。
- **干净卸载**：卸载包 = 所有注册项一起消失，不留垃圾。
- **Win11 主菜单硬性要求**：[前面讲过](https://daiw.org/manual/windows-shell-ext/win11-menu)，要进 Win11 那个新版右键主菜单，**必须**打包 + 用 `IExplorerCommand`,`regsvr32` 的老菜单只会被折叠进“显示更多选项”。

所以现代扩展的典型形态是：**`IExplorerCommand` 实现 + MSIX/稀疏包声明**。老的 `IContextMenu` + `regsvr32` 仍然能用，但只活在“更多选项”的二级菜单里。

## 四、现代化迁移对照表

这是把老代码搬到 Win10/11 时最该记住的一张表——**左边是本指南参考的 2004 年老文里的写法，右边是现在该用的**：

| 目的 | 旧接口(已过时/受限) | 新接口(Win10/11 推荐) |
| --- | --- | --- |
| 初始化——拿到目标文件 | `IShellExtInit` / `IPersistFile` | `IInitializeWithStream` → `WithItem` → `WithFile` |
| 缩略图 | `IExtractImage` | [`IThumbnailProvider`](https://daiw.org/manual/windows-shell-ext/thumbnail) |
| 列表额外栏目 | `IColumnProvider`(**已移除**) | [属性处理器 + `IPropertyStore`](https://daiw.org/manual/windows-shell-ext/property-handler) |
| 右键命令 | `IContextMenu`(进“更多选项”) | [`IExplorerCommand` + 打包](https://daiw.org/manual/windows-shell-ext/explorer-command) |
| 数据关联 | 直接读文件句柄 | 优先 `Stream`，以便沙箱/云文件/隔离宿主 |

<Callout type="warn">
  一条总原则：**能用 `Stream` 就别用 `File`**。`IInitializeWithStream` 让扩展跑在受限沙箱、云占位文件(OneDrive Files On-Demand)、隔离宿主里都不出问题；直接抓文件路径的老写法在这些场景会失效。这也是微软反复强调的方向。
</Callout>

## 结语

到这里，Windows Shell 扩展的主要扩展点就走完了一遍：从[右键菜单](https://daiw.org/manual/windows-shell-ext/context-menu)、[Win11 新菜单](https://daiw.org/manual/windows-shell-ext/win11-menu)，到[缩略图](https://daiw.org/manual/windows-shell-ext/thumbnail)、[图标](https://daiw.org/manual/windows-shell-ext/icon)，再到[属性](https://daiw.org/manual/windows-shell-ext/property-handler)、[提示](https://daiw.org/manual/windows-shell-ext/infotip)、[属性页](https://daiw.org/manual/windows-shell-ext/property-sheet)、[预览](https://daiw.org/manual/windows-shell-ext/preview)、[拖放](https://daiw.org/manual/windows-shell-ext/drop-handler)、[图标叠加](https://daiw.org/manual/windows-shell-ext/icon-overlay)。每一章的完整可编译代码都在 [shell-extension-demos](https://github.com/davidapp/shell-extension-demos) 仓库里。

核心心法就三条：**扩展跑在别人进程里，所以要快、要稳、要隔离**；**能用 Stream 初始化就别碰文件路径**；**面向 Win11 就上 `IExplorerCommand` + MSIX**。

## 参考

- [Shell 扩展性总览](https://learn.microsoft.com/zh-cn/windows/win32/shell/shell-exts) · [初始化 Shell 扩展](https://learn.microsoft.com/zh-cn/windows/win32/shell/handlers)
- [用 MSIX 集成到 Shell](https://learn.microsoft.com/zh-cn/windows/apps/desktop/modernize/desktop-to-uwp-extensions)
- [注册 Shell 扩展处理器](https://learn.microsoft.com/zh-cn/windows/win32/shell/reg-shell-exts)
