作者的话
关于 Windows 外壳扩展方面的文章 私心以为最好的应当算是 Michael Dunn 的 The
Complete Idiot’s Guide to Writing Shell Extensions 我也曾想过 所谓 眼前有景道不
得 崔颢题诗在上头 既然已经有了这么好的文章 我还来饶舌算什么 不过转念再想 文章
虽好 毕竟是为Visual C++的用户看的 对 Delphi的使用者来说似乎有点不公平 我最初编
写 Shell Extension 的时候用的也是 Visual C++ 不过现在已经转而使用 Delphi 觉得两者
毕竟还是有所不同 因此就有了这篇文章 算是将我的一些心得体会和大家分享
我最初的打算是将Michael Dunn 文章中涉及的全部内容全部转成 Delphi 程序 再加上我
自己的一些发现 做成一个完整的系列 不过后来发现这个工程量实在相当的大 而且似乎没有
必要 因为 Windows Shell Extension 的许多内容是相通的 完全可以举一反三 我再重复
MSDN 或者Michael Dunn 文章中的那些东西 似乎是在浪费时间 最终我决定只用一个例子
说明 Shell Extension 编程的基本原理就好 至于后面的东西 那就 修行在各人 了
我是第一次写这样长的文章 而且从文字 程序到图片样样俱全 加上 Acrobat 又不熟悉
用法 所以做的比较辛苦 如果有什么意见或是发现问题的话欢迎来信告诉我 不过我无法保证
一定能够回信 如果想要转载的话也无妨 不过希望能够尊重我的劳动 不要擅自修改文章内容
也不要改头换面署上自己的名字
再次感谢您费心阅读本文
weizhisheng
2002 年 5 月 3 日
第一篇 概述
尽管 Windows 资源管理器的功能在每个新版本中都得到了不少增强 还是有许多人对它感
到不满意 有没有办法让资源管理器变得更好用 更符合自己的需要呢 一个办法就是自己重新
打造一个全新的 Explorer 目前已经有了一些这方面的软件 比如 PowerDesk Utilities 和
Turbo Browser 就堪称个中翘楚 不过 要完全实现资源管理器方方面面的功能 其工作量可
能超乎想象 而且牵涉的知识面颇广 对个人来说难度高了一些 而另一个办法就是利用
Microsoft 开放给我们的外壳扩展接口了 虽然这种途径限制更多一些 但是门槛比较低 而且
也能够满足绝大部分需要 这方面一个最好的例子就是WinZip 这个软件几乎把外壳扩展的功
能发挥到了极致 相信你已经很熟悉它了 在本文中 我就利用自己完成的一个实际的例子 来
说明如何编程扩展 Windows 外壳
为了完成这个例子 我参考了一些资料 主要是 Michael Dunn 的 The Complete Idiot's
Guide to Writing Shell Extensions 可以从http://www.codeproject.com/shell/ 得到
这个系列的文档 这是我看到的最好的介绍外壳扩展编程的文章 感谢Michael Dunn 不过
他的例子是用 Visual C++编写的 我在阅读的时候就感到用Visual C++来编写这些东西显得
太过繁琐 而且将 MFC/ATL/STL 混合在一起的风格也让我觉得非常不爽 因此 后来我改用
Delphi 重写了程序 这样确实为我节省了不少工作量 如果你常用的工具是 Visual C++ 那
么建议你还是应该去阅读 Michael Dunn 的文档 这些文档内容更完整得多 我的这篇文章主
要是面对 Delphi 的用户 提供一个入门级的 Windows 外壳扩展编程指导
我用来编写这个程序的平台是Microsoft Windows 2000 Professional 编程工具是 Borland
Delphi 6.0+Update Pack 2 在编写外壳扩展程序的时候 我推荐尽可能使用最新的开发平
台 因为 Windows Shell 的接口总是在持续的更新 而比较老的开发平台 例如 Delphi 5.0
和更早的Visual C++ 6.0 将无法识别许多新的结构 接口和函数等等 虽然我听到不少抱怨
说 Delphi 6.0 不如早期版本来的稳定 不过至少在开发这个程序的过程中 它并没有给我造成
什么麻烦 至于操作系统 无论如何要用 Windows 2000 因为在 Windows 9X 下调试外壳
扩展是一件非常麻烦的事情
在编写外壳扩展之前应该先做一些准备工作 首先必须在注册表中作一些改动 因为任何外壳扩
展都是作为 DLL 而加载到 Explorer 的进程空间内的 所以如果不做些手脚 那么只要 Explorer
还存在 你编写的外壳扩展就无法顺利编译 如果你愿意手动修改注册表的话 可以参考Michael
Dunn 的文章 不过 我建议你利用 Windows 优化大师 这个软件帮你做掉这项工作 只要
选中 启动系统时为桌面和 Explorer 创建独立的进程 即可 这个选项会增加一些系统开销
不过从理论上来讲倒是可以让操作系统更稳定一些 如下图所示
另外一个问题就是 在调试外壳扩展的时候 你不能太依赖于集成调试器 就拿 Context
Menu 扩展来说 你怎么能一方面激活集成调试器 另一方面又让资源管理器中的上下文菜单保
持可见呢 所以 你首先应该养成在运行程序之前把程序先好好检查一遍的习惯 不要急着按
F9 其次 如果你需要一个脱离 IDE 又能够显示调试信息的工具 那么有一个很好的工具
DebugView 可以满足你 这个软件可以从 www.sysinternals.com 取得 我发现 这个工
具至少能够解决 90%以上的调试需求 它已经成为我的编程工具箱中最重要的工具之一
最后再罗索两句 编写外壳扩展的时候一定要特别小心 尽量处理任何可能发生的错误 因
为外壳扩展是被 Explorer加载到进程空间内的 所以外壳扩展中的任何错误都可能让 Explorer
崩溃掉 特别是你的程序中如果用到任何VCL 类或者 RTL 函数的话 一定要处理掉可能发生的
异常 因为操作系统并不知道如何处理 VCL/RTL 异常 其后果如何是可想而知的 考虑到
Explorer在系统中的地位 你应该有一种如临深渊 如履薄冰的感觉了 另外 为了用户考虑
外壳扩展所执行的任何任务都应该尽可能快的完成 决不要用外壳扩展执行那些需要很长时间的
动作 否则的话 如果用户在资源管理器中点击鼠标后要好几秒钟才会看到菜单出现 那么很快
他们 她们 就会感到不耐烦 进而对你的软件失去信心
准备好了吗 我们出发吧
第二篇 建立程序框架
外壳扩展有好几种类型 在这里 我要实现的是一个 Context Menu 扩展 因为这是最常
见 最有用的扩展类型 而且 所有的外壳扩展都有许多相通的地方 学会一种以后 其他的也
就非常容易掌握了
我计划让这个扩展完成如下的一些功能
1 对任何文件 都能够实现 Copy(Move) to Anywhere Windows 资源管理器
并不直接支持这项功能 不论是 Cut/Copy&Paste 或者是开两个文件夹窗口来
Drag/Drop 都要经历多个步骤才行 毕竟麻烦 我是在工具软件 Nuts & Bolt
中第一次看到这个功能的 当时就觉得它非常有用 不过一直不知道是如何实现
的 现在好了 我们也来 DIY 一回
2 对于 COM 组件库 能够实现 Register/Unregister 的功能 凡是编程的人都应
该知道这个内容 从而不必动用不讨人喜欢的 regsvr32
3 对于图片文件 能够在 Context Menu 中预览 用过PicaView 吗 对了 就是
它 如果只是想知道图片的概貌 又何必非ACDSee 不可 Windows 2000 的
缩略图模式处理图像太慢 而且占用太多资源 我也不喜欢
上述三种情况 几乎涵盖了 Context Menu 扩展所能遇到的所有情况 如何处理单一文件
如何处理多个文件 如何管理自绘式 Owner-Draw 菜单 可以说 只要能妥善处理这三种
情况 那么在 Context Menu 扩展中再没有什么困难的问题了
因为任何外壳扩展首先必须是一个 COM 组件 所以我们就从这里开始
1 用 Delphi 新建一个 ActiveX Library 并保存 我用的名称是 YHShellExt 你当
然可以猜到 YH是我的名字的缩写 你可以把它换成自己的名字
2 再次用 Delphi 新建一个 COM Object 在 COM Object Wizard 中 将对象命名
为 YHContextMenu Options 中的两个检查框都可以不必选中 其他的保持默认
即可
现在这个程序的框架已经建立起来了 Delphi 为我们自动产生了 TYHContextMenu 类的
骨架代码 并且在单元的 initialization 部分自动产生了一个 TComObjectFactory 对象 这个
对象可以完成 COM 组件的注册工作 不过 对于外壳扩展来说 除了注册COM组件之外还必
须完成一些额外的工作 这个组件才具备了外壳扩展的身份 所以我们还需要从
TComObjectFactory 派生一个类才行 对代码稍作修改 完成后应该类似下面这样
unit YHCMImpl;
interface
uses
Windows, Messages, ActiveX, Classes, SysUtils, ComObj, ShellAPI, ShlObj,
Graphics, JPEG, Registry;
type