65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 .NET 的 Explorer Shell 扩展(修订版)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (12投票s)

2015 年 9 月 16 日

CPOL

22分钟阅读

viewsIcon

38280

downloadIcon

1926

用于基于 .NET 的 Shell 上下文菜单的框架,使用 VB、C#

引言

本文将提供来自以下文章的更新或增强版本代码:如何使用 .NET 语言编写 Windows Shell 扩展,作者 MS-Pl (Microsoft),并附带关于如何为您的应用程序自定义和实现它的分步说明。所有艰苦的工作都已完成 - 更新以使其更易于实现。

所有版权、警告和附加文件仍保留在代码和项目中,它们仍是 MS-Pl 的作品。

背景

引用的文章涵盖了实现一个用于 **Explorer 上下文菜单**的 **Shell 扩展**的要求,并包含了一个 VB 和 C# 的演示项目。将演示项目重构为实际扩展并发送多个文件的步骤有些不清楚。

在重构该代码库以用于某些目的的过程中,出现了一些问题并进行了几项改进。代码还被重构,使其能够充当一个模板或插件,只需更改几个变量的值即可轻松自定义,以实现每个应用程序的特定需求。

模型是一个帮助程序 DLL,它提供上下文菜单,并启动一个关联的 EXE,通过命令行传递选定的文件。

还增加了动态和自注册功能。这允许您的主应用程序注册 DLL 或根据需要注册不同的扩展。此功能由新的 ShellReg 帮助类提供,您的主应用程序将使用它。

您真的需要 Shell 扩展吗?

由于 Explorer 的上下文菜单可以轻松实现而无需 Shell 扩展,因此答案可能实际上是“否”。

在本文末尾,对基于 .NET 的 Shell 扩展的一些问题进行了简要概述。在实现 .NET Shell 扩展(而不是标准上下文菜单)之前,您应该了解这些问题。鉴于需要 Shell 扩展,此处提供的修改后的代码模板应该可以相对轻松地实现。

在此过程中,出现了使基于注册表的(标准)上下文菜单更易于使用的手段,从而产生了配套文章 Explorer Context Menu Manager,这是一个帮助类,用于从您的应用程序添加、更改和删除基于注册表的 Explorer 上下文菜单。

更改摘要

  1. 最值得注意的是能够注册多个扩展、启动“主”应用程序而不是仅显示对话框,以及将多个文件传递给目标应用程序。
  2. Option Strict 的一些修复(主要是 .ToStringConvert.Toxxxx)。
  3. 原始代码未预见自定义扩展(“.foo” vs “.txt”)。因此,在 RegisterShellExtContextMenuHandlerUnregisterShellExtContextMenuHandler 方法中可能会出现 NullReferenceException。我认为这仅限于 VB 版本。
  4. 通常,Explorer 会在 System 目录中启动帮助程序 DLL;您的 DLL 反过来会在同一目录中启动主应用程序。这一点已修改,以便以它们所在的文件夹作为当前目录来启动两者,这样就可以找到任何其他必需的 DLL。
  5. 添加了一个简单的日志记录器用于调试。
  6. 添加了一个演示/测试 MainApp。该应用程序仅显示传递的参数(文件),但允许您轻松验证基本框架是否正常工作。除了充当测试目标外,它还包括自注册的演示代码。
  7. 已修复参数处理,使其能够正确处理嵌入空格。
  8. 已添加代码(希望)能够渲染背景伪透明。
  9. 将 VB 和 C# 项目合并到一个解决方案中。VB 和 C# 的 DLL 输出被制成可互换的;主要是调整了 VB 的 Namespace 方法。
  10. 最重要的是,重构了代码以使用一组控件变量,这样在大多数情况下,唯一需要的代码修改就是更改其中一些值。它们位于 FileContextMenuExtMyAppShellMenu 类的顶部,作为一个块。

新增功能包括动态自注册:从相关的应用程序注册、取消注册或修改与您的 Shell 扩展 DLL 关联的扩展。

本文将侧重于实现此基本框架所需的步骤,而不是重复以前的内容。要更全面地基本理解 Shell 扩展,请阅读原始文章

非常重要的说明

对原始代码库的更改主要是为了使其更容易快速生成可工作的 **Shell 扩展**助手。但“更容易”并不意味着“微不足道”。与典型的 VB/NET 项目不同,编译和测试 Shell 扩展是繁琐的:一旦您右键单击一个扩展,您的处理程序就会被 Explorer 使用,这会阻止您重新编译和替换旧版本。取消注册它并不会释放它。

花时间阅读并消化文章和步骤。在编译和测试之前,请仔细而周到地应用您的代码更改。有关调试和用法的其他说明将在分步说明之后提供。

Using the Code

项目设置

该框架主要用作主应用程序的独立帮助程序 DLL。例如,一个专用的 MyAppShellExt.DLL,它与 Explorer 一起工作以启动 MyMainApp.EXE,通过命令行传递选定的文件。这是一个相当常见的模型——您经常会看到存储在 EXE 文件中的 xxxShell.DLL 文件来提供这种类型的服务(尽管原因有时是因为 DLL 使用不同的语言)。

Explorer 将使用 System 文件夹作为工作目录来启动您的 ShellExt DLL。为了能够找到要启动的相关应用程序,ShellExt DLL **必须**与应用程序位于同一文件夹中。

1. 更改程序集名称

  • 转到 **项目属性 -> 应用程序** 并更改程序集名称(这不是绝对必需的,但让 DLL 名称与主应用程序相关是有意义的。)
  • 可选地,您可能还想更改程序集信息,以便它引用您的工作。
  • 请勿更改 .NET Framework 版本。它必须是 **.NET 4.0** 或更高版本(有关详细信息,请参阅原始文章)。

2. 配置项目以支持 AnyCPU

  • 这将允许 DLL 在 32 位或 64 位操作系统上注册。
  • 如果 MainApp 必须作为 32 位运行(可能它使用 32 位 DLL),请将目标应用程序编译为 x86。据我所知,还没有人找到一种确切的方法让 64 位进程强制 AnyCPU 应用程序以 32 位进程(或反之)启动。

为其余更改打开 ContextMenuExt.vb|cs。无需更改任何其他文件。

    <Guid("679944C8-FEC8-446A-8089-64E0DE515898"),
    ClassInterface(ClassInterfaceType.None),
    ComVisible(True)>
    Public Class MyShellMenu
        '...

3. 创建您自己的类 GUID(请参阅工具菜单,创建 GUID)并替换列出的 GUID。

新的 GUID 确保您的处理程序被唯一标识。

4. 更改类名

演示/框架使用 MyShellMenu。您可能希望名称与关联的应用程序相关

主应用程序:FizzBar.EXE
程序集:FizzBarExt.DLLFizzBarShell.DLL
类:FizzBarContextMenuFizzBarMenu

我认为这些不必是唯一的,因为 GUID 用作标识符。但是,有意义的名称有助于将程序集类与它们关联的主应用程序相关联。如果您使用调试日志,这一点尤其正确。

VB IDE 管理 Namespaces 的方式与 C# 项目不同。为了使 VB 和 C# 的 DLL 可互换,VB DLL 项目以更类似 C# 的方式实现 Namespaces。也就是说,它们在 DLL 项目的两个类中都手动声明。

通常,没有理由更改 C# 或 VB 中的 NameSpace

控件变量

要控制或更改各种选项,在 MyShellMenu 类的顶部有一个控件变量块。该部分如下所示

    <Guid("679944C8-FEC8-446A-8089-64E0DE515898"),
    ClassInterface(ClassInterfaceType.None), ComVisible(True)>
    Public Class MyShellMenu
        Implements IShellExtInit, IContextMenu

        ' Everything you may want to change should be between the star bars
        '*****  *****  *****

        ' {PL} list of file exts to associate with this handler
        Private Shared exts As String() = {".txt", ".foo", ".log", ".lst"}

        ' The base name of the app to run - do not hard code a path!
        ' The code will figure out where it is.
        Private AppName As String = "MyMainApp.exe"

        ' menu text and such
        Private menuText As String = "Open in MyMainApp"
        Private verb As String = "loadfiles"
        Private verbCanonicalName As String = "OpenInMyMainApp"
        Private verbHelpText As String = "Open Files in MyMainApp"

        ' If you want to send only the file clicked on, change to TRUE
        Private FeedFirstFileOnly As Boolean = False

        ' simple step logger for debugging...you'll find it in MyDocuments
        Private LogFile As String = "ShellExtDebug.log"
        ' Be sure to set to false for release version
        Private LogActive As Boolean = False
        ' Start a new Log each time or append
        Private LogAppend As Boolean = False

        Private AddSeperator As Boolean = False

        ' Resource name of the image you wish to use
        Private bmpName As String = "starBlue"

        ' oldBackColor == the back color of the Resource image,
        '   typically Transparent or Fuchsia
        ' newBackColor == Color to change all oldBackColor pixels to
        '   Nothing (default) == use default ContextMenuStrip.BackColor
        Private oldBackColor As Color = Color.Transparent
        Private newBackColor As Color? = Nothing           
        
        ' end of stuff to change region for simply sending files
        '*****  *****  *****
        
        ' {PL} The names of the selected file
        Private selectedFiles As List(Of String)

        Private targetApp As String = ""
        
        Private menuBmp As IntPtr = IntPtr.Zero
        Private IDM_DISPLAY As UInteger = 0
        
        Private Shared AsmHandlerName As String = ""
        

        Public Sub New()     ' in C#:  public MyShellMenu()  
           ...

请注意,如果您遵循了上面的项目级说明,GUIDClass 名称将会不同。

描述
Private Shared exts As String() = {...}

这是您的 ContextMenu 需要响应的扩展列表。如果只有一个,则只列出一个数组字面量。在注册 DLL 时使用此设置,因此请将其保留为数组,保留为 Shared 并使用小写。

Private menuText As String = "Open in MyMainApp"
Private verb As String = "loadfiles"
Private verbCanonicalName As String = "OpenInMyMainApp"
Private verbHelpText As String = "Open Files in MyMainApp"

这些是用于菜单显示和标识您的菜单处理程序的文本等。

Private FeedFirstFileOnly As Boolean = False

可能存在一些特殊情况,即有人只想将单个文件发送到 MainApp,即使选择了多个文件。在这种情况下,将 FeedFirstFileOnly 设置为 True。用户单击的文件应该是唯一发送的文件。(如果您只发送一个文件,您实际上应该实现一个简单的上下文菜单。)

Private LogFile As String = "MyShellExtDebug.log"
Private LogActive As Boolean = False
Private LogAppend As Boolean = False

由于 Shell 扩展调试起来可能很麻烦,因此提供了一个简单的日志机制来报告主要涉及方法的进度。LogFile 是要使用的文件名,将在“我的文档”中创建。一旦您的菜单处理程序按您想要的方式工作,您可以将 LogActive 设置为 FalseLogAppend 控制是每次会话都附加日志,还是只包含上次运行的报告。

不要假设所有启动失败都是由于 **Shell 扩展**中的问题!在多次注销 Shell Ext 并重启 Explorer 之后,问题实际上是主应用程序中的“微小改进”引起的。MyMainApp 项目用于帮助验证核心扩展 DLL 是否正常工作。

Private AddSeperator As Boolean = False

一个简单的 True/False,用于确定是否要在条目后添加分隔符。

Private bmpName As String = "starBlue"

如果您想在菜单上使用的 16x16 图像名称(如果有),在资源中。设置为空 string 以省略图像。

Private oldBackColor As Color = Color.Transparent
Private newBackColor As Color? = Nothing

无论格式或方法如何,图像似乎都无法以透明背景绘制。这在 MS 代码中并不明显,因为他们使用了几乎没有背景的图像。对 Shell 扩展的非正式调查显示,许多扩展都存在同样的问题,包括 Adobe。

解决方案,如果存在的话,似乎是手动将任何透明像素映射到菜单的背景颜色。但是,关于 Explorer 或系统菜单以及如何获取它们的 BackColor 的信息几乎找不到。当使用主题时,该值也可能被忽略。

一个似乎有效的方法是使用 ContextMenu 的默认背景颜色。前提是 .NET/Windows 在某个地方使用相同的默认颜色用于所有此类菜单,无论是否有主题。(这可能并非普遍适用,我的操作系统版本和主题组合有限,无法确定。)

**newBackColor** 是用于替换 oldBackColorBackColor。默认值为 Nothing,表示使用 ContextMenu 的默认 BackColor

**oldBackColor** 是图像中的 BackColor。通常,在位图上,它是一个占位符,如 Color.Fuchsia,尽管 Transparent 也适用于简单、干净的 PNG 文件。

如果您获得有关此的更多信息,可能只需要更改 **GetPseudoTransparentBitmap()** 函数。

**给 VB 开发者的注意事项**:如后面所述,使用 **C#** 版本可能有一些性能优势。上面的变量块几乎相同,但修改 **C#** 版本还需要将构造函数和析构函数更改为使用新的类名

    public MyShellMenu()        //  constructor
    {   
    ...

    ~MyShellMenu()                // destructor
    {

如果您将类名更改为 FizzBarMenu,则两者都必须更改

    public FizzBarMenu()        // case sensitive!
    ...
    ~FizzBarMenu()

就是这样!调整这几个变量应该足以将通用模板转换为可工作的 Shell 扩展。下一步是编译和测试您的扩展助手和主应用程序组合。请阅读以下主题以获取更多信息。

创建测试文件夹

切勿从 VS 调试文件夹运行或注册您的 Shell 扩展。这会给您一个关于它工作得有多好的错误印象,并且会阻止您重新编译 Shell Handler。一旦调用,Shell 扩展就会一直被 Explorer 使用,直到您重新启动/重启 Explorer。取消注册不会导致 Explorer 释放它。

使用类似于运行时环境的测试文件夹,例如 C:\Program Files\Test Project

请注意,您不能简单地从新文件夹(例如,C:\Program ...\prj name 2)测试新编译的版本,除非您还要创建一个全新的处理程序(GUIDAssembly.Class Name 等)。

注册

您需要注册您的 Shell Ext 进行测试。MS-PL 项目包含一些 VS2010 安装程序项目,但在 VS2012 及更高版本中不提供这些项目。

要注册您的 DLL:找到您 .NET 版本的 RegAsm(通常是 Microsoft.NET\Framework64\v4.0.xxxx)。如果您从 **开始菜单 | Visual Studio Tools | Developer Command Prompt** 启动命令窗口,它应该会在路径中包含正确的 RegAsm。务必以管理员身份运行,如果您的操作系统是 64 位,请打开一个 x64 窗口。

在 **具有**管理员权限的命令窗口中,导航到您的 Shell DLL 和相关应用程序所在的位置

   regasm MyAppShellExt.dll /codebase

这将您的 ShellExt 类/DLL 与代码中指定的扩展名相关联。您需要

在部署应用程序时通过安装程序或脚本执行相同的操作。要 UnRegister

   regasm MyAppShellExt.dll /u

如果您收到 DLL 不是有效 .NET 程序集的错误,这可能意味着程序集存在比特匹配问题。如果 RegAsm 报告成功,但您的条目未显示在上下文菜单中,则您可能在 64 位操作系统上使用了 x86 窗口/RegAsm。

一旦您测试了您的 Shell 扩展,**Explorer** 将会打开它,并且您将无法将其新的构建复制过去。不幸的是,您不能仅仅关闭文件资源管理器。

扩展排除

当用户右键单击具有您的 Shell 扩展支持的扩展名的文件时,将显示您的菜单。但是,可能会选择许多文件,其中一些文件的扩展名未由您的应用程序注册。这些“其他”文件也会(当 FeedFirstFileOnlyFalse 时)传递给您的应用程序。

这不是一个 bug。所有上下文菜单处理程序代码都只关心单击时鼠标下的文件。

可以在传递给主应用程序之前按扩展名过滤掉不支持的文件。对我来说,最好保持 DLL 简单,并在实际应用程序中处理任何过滤,因为应用程序可以随时更新,并且比帮助程序 DLL 更容易。让您的应用程序提供健壮的处理,例如“仍要尝试?”对话框,以允许用户知道应用程序将起作用的情况,尽管您可能没有预见到它。

调试 Shell 扩展

这通常有效...

  • 更改为 Debug 配置;在 64 位操作系统上选择 x64
  • 在代码中设置所需的断点
  • 在“工具”或“调试”菜单上(我的菜单上都有),选择“附加到进程”,
  • 从进程列表中选择“Explorer.exe

您必须以管理员身份运行 Visual Studio。我无法弄清楚为什么有时它不起作用,但可能是因为 VS 未以管理员身份运行或配置错误(x86)。

Explorer 现在将作为您的测试项目来调用您的 Shell 扩展。对于比仅传递命令行执行更多操作的 DLL,这更有用。传递文件非常简单,因此日志文件甚至 MessageBox 通常就足够了。修改后的代码提供了简单的日志记录。

如何停止/重启 Explorer 进行测试

一种方法是注销然后立即重新登录。这将重置 Explorer,但还需要关闭所有应用程序和进程。

- 或 -

  • 使用您的管理员命令提示符窗口
     taskkill /F /IM explorer.exe
  • 取消注册旧版本
  • 从 **非管理员** 命令窗口(请参阅注释)启动一个新的 Explorer
     start C:\Windows\explorer.exe
  • 将新版本复制到您的测试文件夹并重新注册。

重要提示

如果您从具有管理员权限的命令窗口重启 **Explorer**,从 **开始菜单**启动的应用程序将继承这些管理员权限,这可能不是您想要的。环境变量文件夹路径可能解析为您的正常 Windows 登录以外的内容!

杀死 Explorer 也会停止所有其他扩展类型应用程序并移除许多系统托盘图标。

动态和自注册

演示包含一个初始类,允许目标应用程序注册/取消注册 Shell 扩展 DLL。这可以允许您的主应用程序作为用户选项来切换其 **Explorer 上下文菜单**。

此功能的另一个用途是允许用户选择所需的上下文菜单扩展。例如,从列表 {".txt", ".foo", ".log", ".lst", ".bar"} 中,用户可以选择除“.foo”之外的所有项,或者仅选择“.log”与您的上下文菜单关联。

ShellReg 类

ShellReg 是一个帮助类,供您的 MainApp 调用您 shell 扩展 DLL 中的注册方法。在所有情况下,使用这些方法的应用程序(通常是您的 MainApp.exe)必须位于目标 DLL 的同一目录中。请注意,ShellReg 调用您 Shell 扩展 DLL 中的新方法,如果您更改其中一个的名称,可能会导致问题。

ShellReg 方法

每种方法都需要 DLL 和上下文菜单类的名称,这些名称在构造函数中传递

Private asmName As String = "MyShellExt.dll"
Private className As String = "ShellExtContextMenuHandler.MyShellMenu"
...
Dim myShReg As New ShellReg(asmName, className)

请注意,Namespace 是类名称不可或缺的一部分。

Sub RegisterShellExt()

标准注册您的上下文菜单帮助程序 DLL。示例

 myShReg.RegisterShellExt()

这将尝试以提升的进程运行 RegEdit 来注册您的 Shell 扩展 DLL。如果用户拒绝权限,它将无法运行。

Sub UnRegisterShellExt()

取消注册您的 shell 菜单类(例如 MyShellMenu)。示例

  myShReg.UnRegisterShellExt()

这同样会尝试以提升的进程运行 RegEdit 来取消注册您的 Shell 扩展 DLL。如果用户拒绝权限,它将无法运行。

Function GetShellExtList() As String()

获取您 DLL 类中定义的扩展的 static 列表。这可以防止您在 MainApp 中编码第二个列表,从而可能导致列表不匹配。

  Dim allExts As String() = myShReg.GetShellExtList()

Sub RegisterExtensions(newExts As String())

这将(重新)注册您的上下文菜单类与传递的那些扩展,这些扩展 **也** 在主共享/静态列表中。也就是说,如果 MyAppShellExt 中的扩展列表是 {".txt", ".foo", ".log", ".lst"},那么该列表的任何子集都可以传递。

您 **不能** 动态扩展列表,例如添加“.baz”,因为没有(容易的)方法以后移除该扩展关联。为了注册新的(子)集扩展,旧列表会先被取消注册(该方法会为您完成)。从定义的列表开始,比检查注册表中的每个扩展以防添加新扩展要更容易、更简单、更快。DLL 中的共享扩展数组是主列表。

   Dim newExts As String() = {".txt", ".log", ".lst"}
   ...
   myShReg.RegisterNewExtensions(newExts)

这依赖于我添加到核心 Shell 注册代码中的方法。它并非所有 Shell 扩展都标准具备。

这同样需要管理员权限,因为它最终会更改注册表中的 HKCR。但是,应用程序可以通过调用 Shell 扩展 DLL 中的新方法来执行此操作,因此呈现给用户的 UAC 将是您应用程序的 UAC。

最好的理解方式是检查 MyMainApp 代码。TestForm 中的按钮单击事件会以 /reg 参数启动 MyMainApp 的新提升实例。

现在,切换到 Program.vb 中的 Sub Main。当检测到 args() 中的开关时,它不会启动 WinForms 应用程序,而是调用 Shell 扩展 DLL 中的相应方法,然后终止。(这假设最终用户授予 MyMainApp 以提升权限运行的权限)。在某些情况下,创建一个小的控制台应用程序来执行注册表任务会更简单,但此方法避免了另一个辅助程序集。

这些工作得很好,但有一些限制

  1. 与之前一样,将要使用这些的应用程序必须是 64 位进程(如果操作系统是 64 位),否则注册表操作将不起作用(或者说,它们将不正确,Explorer 看不到)。
  2. 如前所述,任何想要使用 ShellReg 方法的应用程序都必须具有提升的访问权限,因为它会更改注册表。请参阅演示,了解如何“显示”为当前应用程序请求提升的权限。
  3. UnRegister 似乎每次都能成功禁用相关的 ContextMenu 助手。

演示

演示更像是一个测试应用程序。它不是为从 VS IDE 调试器运行而设计的。如上所述,您不想在 VS 项目文件夹中注册文件作为 Explorer 扩展。而是编译应用程序,然后将其和相关的 DLL 复制到测试文件夹。

DLL 需要与 **Explorer** 注册,并且 DLL 希望在与它相同的文件夹中找到相关的 EXE 来启动。通常,扩展会在 Windows\System 文件夹中启动,该文件夹又会在同一文件夹中启动目标应用程序。前面提到的一个更改是,扩展 DLL 更改了当前目录,以便在启动目标应用程序时可以找到任何*其他*依赖项。要做到这一点,它们需要位于同一个文件夹中。

文件当然可以放在任何地方。一些简单的、空的带有各种扩展名的文本文件可以在 zip 文件中的 Test Files 文件夹中找到。

测试应用程序简单而基础。它不是单实例的,也不会将命令行转发给现有实例。但是,这在 配套文章 和其他地方(例如此 StackOverflow 问题)有所涵盖。

摘要

原始文章的重点在于实现 ContextMenu 提供程序的 Shell 扩展所需的接口和代码。因此,演示代码以一种相当直接的方式说明了这一点,而不考虑代码重用。

本次修订的目的是将该代码转换为一种可重用的模板,以便轻松快速地实现自定义菜单。除了创建唯一名称和标识符的几个步骤外,还可以通过更改一小块变量来完成。

请务必小心您需要做的更改。如前所述,容易并不意味着微不足道。

项目文件

CSShellExtension

C# 项目文件,用于创建演示 Shell 扩展处理程序

MyMainApp

VB 项目,用作主应用程序的占位符,并显示通过命令行传递的文件。可与任一(或两者)Shell 扩展 DLL 一起使用。

VBShellExtension

VB 项目文件,用于创建演示 Shell 扩展处理程序

VB 和 C# 项目都包含原始文章/项目中包含的安装程序文件。还包括原始的 ReadMe。

资源和参考文献

附录:基于 .NET 的 Shell 扩展

有许多文章警告使用托管代码(.NET)进行 Shell 扩展。Microsoft 的 All In One Code 网站上关于演示 .NET Shell 扩展的一系列文章似乎自相矛盾,但所有这些基于 .NET 的 Shell 扩展系列似乎都被撤回了。其中关于上下文菜单扩展的文章被重新发布在此处,作为本文的基础。

文章

建议阅读这些文章,但要点似乎是

  1. .NET 扩展必须加载 .NET 运行时,这会减慢 Explorer 的速度
  2. CLR 的各种问题以及它的工作方式与 COM、接口和非托管主机相冲突
  3. 取决于具体的扩展类型(有几种),其中一种或另一种的可能性会增加
  4. 某些扩展类型可能存在资源处置问题

阅读这些文章并自己决定如何进行。以下是一些明显的建议

保持简单

Shell 扩展有许多类型,大多数都比启动应用程序的 **上下文菜单**处理程序更具野心。**缩略图处理程序**、**属性表处理程序**或包含许多表单和类以直接提供所需服务的扩展,似乎更有可能遇到问题。

**上下文菜单**启动器中没有太多东西:它提供了一个描述菜单项的结构,然后在单击该菜单项时进行响应。尽管如此,一旦实例化,它就成为系统 Shell 的一部分,并且每个应用程序在使用诸如 OpenFileDialog 之类的东西时都会继承它们。

Net Shell 扩展**确实**更慢——使用 1 或 2 个就会注意到性能下降。Shell 扩展开发、测试和调试都很麻烦。仅凭这一点就足以避免它们。

使用简单的上下文菜单

如前所述,基本 Explorer 上下文菜单与 Shell 扩展提供的菜单之间几乎没有区别。原生上,Shell 扩展的唯一优点是可以将多个文件发送到单个目标应用程序实例。VB 尤其使得“转发”参数给应用程序的第一个实例非常容易。

配套文章 Explorer Context Menu Manager 提供了一个帮助类,用于使基于注册表的静态**上下文菜单**更具灵活性,以便在许多情况下可以避免使用**Shell 扩展**。

使用 C# 版本

为了最小化 Explorer 需要加载的内容,请考虑使用 C# 版本。这应该可以避免加载额外的 VisualBasic 相关程序集。对 Explorer 内存使用情况的简单观察似乎表明 C# 版本内存占用约减少 22k。

上述代码的 C# 版本布局相同,只需一个额外的步骤即可自定义使用。变量块相同,您可以将 VB 版本作为罗塞塔石。这并不是一项艰巨的任务。

总而言之,最好将 .NET Shell 扩展仅作为最后的手段。在大多数情况下,简单的上下文菜单不仅更简单,而且在透明背景方面也没有问题,并且可以正常工作。

历史

  • 2015.09.09 - 初始发布
© . All rights reserved.