如何用C++编写一个Shell扩展?

18

这似乎是一个常见的问题,但在搜索了一番后,我没有真正找到我的答案。这里有一篇文章:

http://www.codeproject.com/KB/shell/shellextguide1.aspx

但它是针对非常旧版本的Visual Studio。我正在使用VS 2008,因此指示和界面似乎与我看到的不匹配。

我想使用C ++创建一个简单的shell扩展,为扩展名为.GZ的文件创建上下文菜单。当右键单击这些文件时,我应该能够单击我的上下文菜单项,并在代码中回调以执行某些操作。

其他上下文菜单项将执行诸如生成无模式对话框以接受用户输入,然后执行某些操作等任务。

从我所看到的,这需要使用ATL,但我从未使用过ATL,因此所有对象类型和接口都让我感到非常困惑。如果我有一个合适的教程或文档可以阅读,那么情况就不会那么糟糕了。

有谁能帮帮我吗?难道没有什么不是10年前的教程吗?


1
这实际上是一件特别困难的事情。我为用户右键单击打印机时开发了一个上下文菜单项。尽管我的代码只有40行左右,但大约有1,000行生成的代码。我希望我能提供更多帮助,但我不能分享任何我拥有的代码。作为提示,你的Shell扩展必须驻留在DLL中,并且必须有自己的GUID,并且需要使用regsvr32注册DLL。你想要实现BOTH IContextMenu和IShellExtInit COM接口。有一种方法可以让VS为您生成大部分代码,但我记不清楚了。 - dreamlax
Shell扩展几乎总是基于接口的,这就是为什么项目向导会将它们生成为这样的原因。您还需要实现两个接口来提供Shell扩展(这对于上下文菜单扩展是必需的),并且必须注册为这样做。您无法真正避免接口,所以最好接受这个事实,您将不得不使用它们。生成的代码通过为它们生成代码来保存您手动编写所有繁琐任务的时间。相信每个人都在说什么-让向导完成繁重的工作。 :) - Ken White
1
我不记得具体的ATL实现细节了;我已经很久没有使用ATL或者C++了。:) 不过我还记得当时的头疼情形。你可能会发现这个链接有用;它是为VS 2010 C++特别设计的,演示了一个上下文菜单扩展。这里有一个针对VS 2008 C++的ATL示例 - Ken White
4
10年前的教程也没问题。这段时间里没有什么变化。而且,你甚至不需要一个外壳扩展程序来实现这个操作。你可以通过添加项目到上下文菜单中,并将文件名作为参数传递给可执行文件来完成。你只需通过注册表设置即可实现。这是最简单的入门方式。 - David Heffernan
@DavidHeffernan:不过这种方法也有限制。只有Shell上下文菜单才能提供关于所讨论对象的动态信息。例如,如果你想编写一个Shell扩展程序,创建一个包含zip文件内容的子菜单,仅仅通过注册表设置是无法实现的。 - dreamlax
显示剩余12条评论
1个回答

26

我无法确切地告诉您如何编写Shell扩展,但我会提供一些技巧。编写Shell扩展相对于更简单的“仅限注册表”方法具有一些重要优势:

  • 使用Shell扩展可以动态创建与所选文件相关性更强的上下文菜单项(或子菜单)。例如,如果您正在为zip文件编写Shell扩展,则可以在上下文菜单中创建一个子菜单,其中显示整个zip的内容。
  • 您可以同时处理多个文件,这不仅可能有助于提高性能,而且还可以基于整个选择来确定要执行的操作,而不仅仅是每个文件。

一些Shell扩展的缺点是:

  • 复杂度显著增加。准备花费大量精力来使其正常工作。在计算机旁安装家用咖啡机和/或雇佣人员给您冲咖啡。

  • 调试困难显著增加。同样要喝咖啡。

编写Shell扩展很困难,因为它们很难调试。

  • Shell扩展由explorer.exe进程加载,在没有Explorer的特定配置的情况下,您需要强制退出explorer.exe进程,以便安装新版本的Shell扩展。有一种方法可以让Explorer取消加载不再使用的DLL,但您只应在开发机器上而不是部署目标上执行此操作:

    1. 在RegEdit中,浏览到以下密钥:

      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer

    2. 添加一个名为“AlwaysUnloadDLL”的新DWORD键,并将其值设置为1。

    3. 重新启动资源管理器。

    这通常有效,但仍可能出现需要关闭Explorer因为Shell扩展未被卸载的情况。

  • 请注意,你的Shell扩展可能会被其他应用程序加载,例如如果你在一个应用程序的“打开文件”对话框中右键点击一个文件,则你的Shell扩展将被加载到该应用程序中而不是资源管理器。

  • 如果你的Shell扩展引发运行时错误,通常的结果仅仅是你的上下文菜单项不会显示,很少会告诉你你的Shell扩展未能成功加载或者它导致了运行时错误。

  • 即使有安装程序,配置也可能会很困难,需要在多个位置创建注册表数据,而且根据你希望显示上下文菜单的位置,在注册表中的位置可能因Windows的不同版本而有所不同。

  • 你需要做的事情:

    • Visual Studio 提供了一些快捷方式来创建Shell扩展,但基本上你需要创建一个COM DLL。一个用于上下文菜单项的Shell扩展必须同时实现 IContextMenu 接口和 IShellExtInit 接口。
    • IShellExtInit::Initialize() 方法中,你可以从 IDataObject 参数获取所选文件。从记忆中来看,数据是以“拖放”格式存储的,因此你需要从IDataObject获取一个 HDROP 句柄,并从那里查询文件(这只是从记忆中得出的结论,实际可能与此不同,请谨慎操作)。
    • 一旦你的DLL准备好“安装”,你必须将其复制到某个地方,然后运行 regsvr32 确保它已经注册。
  • 按照此指南,了解放置注册表键的位置。
  • 如果您构建32位DLL,则在64位Windows上可能会出现问题,它可能无法在64位资源管理器中加载...因此,如果您在64位Windows上遇到问题,请记住这一点。
  • 您的DLL实际上将具有与之关联的两个 GUID。我不记得确切的工作原理,但一个GUID是指DLL本身,另一个GUID是指实际的Shell扩展。在创建需要GUID的注册表键时,请确保使用实际Shell扩展的GUID。

  • 综上考虑... (tl;dr)

    权衡一下是否值得使用Shell扩展。如果您想根据所选文件动态创建菜单项,则使用Shell扩展可能是唯一的方法。如果您想同时处理所有文件,则可能也需要Shell扩展。

    除了上下文菜单方法外,还可以在用户桌面或其他地方拥有拖放目标。探索其他用户提交文件给应用程序的方式,因为Shell扩展通常比其价值更高。我用辛苦的方式发现了这一点,我认为其他人也是如此。


    3
    非常棒的帖子,随着.NET 4的出现,编写Shell扩展现在变得更加容易了,您现在可以使用托管代码来实现。在这里提一下,以免人们完全被吓退。 - blak3r
    @blak3r 看起来这是不被鼓励的。 - Tobias Kienzler

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接