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

如何使用 .NET 语言编写 Windows Shell 扩展

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (27投票s)

2011年3月29日

Ms-PL

8分钟阅读

viewsIcon

305361

downloadIcon

13087

C# 代码示例演示了如何使用 .NET Framework 4 创建 Shell 上下文菜单处理程序

关于我们

Microsoft All-In-One Code Framework (http://1code.codeplex.com) 是一个由开发人员需求驱动的免费、集中式代码示例库。我们的目标是为所有 Microsoft 开发技术提供典型的代码示例,并减少开发人员在解决典型编程任务上的工作量。

我们的团队倾听 MSDN 论坛、社交媒体和各种开发社区中开发人员的痛点。我们根据开发人员经常遇到的编程任务编写代码示例,并允许开发人员在短的代码示例发布周期内下载它们。此外,我们的团队还提供免费代码示例请求服务。这项服务是我们的开发人员社区直接从 Microsoft 获取特定编程任务代码示例的一种主动方式。

引言

在 MSDN 论坛中,许多开发人员询问如何使用 .NET 语言(例如 C#、VB.NET)编写 Windows Shell 扩展。

在 .NET Framework 4 之前,使用托管代码开发进程内 Shell 扩展并未得到官方支持,因为 CLR 限制了每个进程只能加载一个 .NET 运行时。CLR 项目经理之一 Jesse Kaplan 在此 MSDN 论坛帖子中对此进行了说明。

在 .NET 4 中,由于可以与任何其他运行时在进程内拥有多个运行时,Microsoft 现在可以为编写托管 Shell 扩展提供通用支持——即使是那些在进程内与计算机上的任意应用程序一起运行的扩展。本文详细介绍了进程内并行功能。但是,请注意,您仍然无法使用 .NET Framework 4 之前的任何版本编写 Shell 扩展,因为这些版本的运行时无法在进程内加载,并且在许多情况下会导致失败。

本文档解释了理论。我到底该如何编写托管 Shell 扩展?

如果您在互联网上搜索,会发现几乎没有 .NET 4 Shell 扩展示例。为数不多的 .NET 2 Shell 扩展示例(由于上述原因不受支持)或多或少存在一些缺陷,例如无法在 x64 环境中加载。为了满足客户需求,我们,All-In-One Code Framework 项目组,将填补这一空白。项目组已计划一系列 .NET 4 托管 Shell 扩展代码示例,涵盖上下文菜单处理程序、属性表处理程序、图标处理程序、数据处理程序、删除处理程序、拖放处理程序、缩略图处理程序、图标处理程序、图标覆盖处理程序等。本文介绍第一个示例:上下文菜单处理程序。

  • CSShellExtContextMenuHandler:Shell 上下文菜单处理程序 (C#)
  • VBShellExtContextMenuHandler:Shell 上下文菜单处理程序 (VB.NET)
  • CppShellExtContextMenuHandler:Shell 上下文菜单处理程序 (C++)

演示

以下是上下文菜单处理程序代码示例的快速演示。在 Visual Studio 2010 中成功构建示例项目 CSShellExtContextMenuHandler 后,您将获得一个 DLL:CSShellExtContextMenuHandler.dll。在 Microsoft Visual Studio 2010 \ Visual Studio Tools 菜单中以管理员身份运行“Visual Studio 命令提示符 (2010)”(如果在 x64 操作系统上,则运行“Visual Studio x64 Win64 命令提示符 (2010)”)。导航到包含生成结果 CSShellExtContextMenuHandler.dll 的文件夹,然后输入命令

Regasm.exe CSShellExtContextMenuHandler.dll /codebase

来注册上下文菜单处理程序。

在 Windows 资源管理器中找到一个 .cs 文件(例如,示例文件夹中的 FileContextMenuExt.cs),然后右键单击它。您将在上下文菜单中看到“Display File Name (C#)”菜单项,以及其下方的菜单分隔符。单击菜单项会弹出一个消息框,显示 .cs 文件的完整路径。

2262.untitled.jpg

实现细节

A. 创建和配置项目

在 Visual Studio 2010 中,创建一个名为“CSShellExtContextMenuHandler”的 Visual C# / Windows / 类库项目。打开项目属性,然后在“签名”页面中,使用强名称密钥文件为程序集签名。

B. 实现基本的组件对象模型 (COM) DLL

Shell 扩展处理程序都是进程内 COM 对象,实现为 DLL。创建一个基本的 .NET COM 组件非常直接。您只需要定义一个带有 ComVisible(true) 的“public”类,使用 Guid 属性指定其 CLSID,并显式实现某些 COM 接口。例如

[ClassInterface(ClassInterfaceType.None)]
[Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)]
public class SimpleObject : ISimpleObject
{
    ... // Implements the interface
}

您甚至不需要自己实现 IUnknown 和类工厂,因为 .NET Framework 会为您处理这些。

C. 实现上下文菜单处理程序并将其注册到特定文件类

实现上下文菜单处理程序

FileContextMenuExt.cs 文件定义了一个上下文菜单处理程序。上下文菜单处理程序必须实现 IShellExtInit IContextMenu 接口。这些接口使用 COMImport 属性在 ShellExtLib.cs 中导入。

    [ComImport(),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("000214e8-0000-0000-c000-000000000046")]
    internal interface IShellExtInit
    {
        void Initialize(
            IntPtr pidlFolder,
            IntPtr pDataObj,
            IntPtr /*HKEY*/ hKeyProgID);
    }
    [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("000214e4-0000-0000-c000-000000000046")]
    internal interface IContextMenu
    {
        [PreserveSig]
        int QueryContextMenu(
            IntPtr /*HMENU*/ hMenu,
            uint iMenu,
            uint idCmdFirst,
            uint idCmdLast,
            uint uFlags);
        void InvokeCommand(IntPtr pici);
        void GetCommandString(
            UIntPtr idCmd,
            uint uFlags,
            IntPtr pReserved,
            StringBuilder pszName,
            uint cchMax);
    }
    
    [ClassInterface(ClassInterfaceType.None)]
    [Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)]
    public class FileContextMenuExt : IShellExtInit, IContextMenu
    {
        public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID)
        {
            ...
        }
         
        public int QueryContextMenu(
            IntPtr hMenu,
            uint iMenu,
            uint idCmdFirst,
            uint idCmdLast,
            uint uFlags)
        {
            ...
        }
        public void InvokeCommand(IntPtr pici)
        {
            ...
        }
        public void GetCommandString(
            UIntPtr idCmd,
            uint uFlags,
            IntPtr pReserved,
            StringBuilder pszName,
            uint cchMax)
        {
            ...
        }
    }

PreserveSig 属性表示在 COM 互操作调用期间发生的 HRESULT retval 签名转换将被抑制。当您不应用 PreserveSigAttribute 时(例如,IContextMenu GetCommandString 方法),方法失败的 HRESULT 需要作为 .NET 异常抛出。例如,Marshal.ThrowExceptionForHR(WinError.E_FAIL); 当您将 PreserveSigAttribute 应用于托管方法签名时,该属性化方法的托管和非托管签名是相同的(例如,IContextMenu QueryContextMenu 方法)。如果成员返回多个成功的 HRESULT 值,并且您希望检测这些不同的值,则有必要保留原始方法签名。

当用户显示已注册上下文菜单处理程序的类的对象的上下文菜单时,上下文菜单扩展会被实例化。

1. 实现 IShellExtInit

上下文菜单扩展 COM 对象被实例化后,会调用 IShellExtInit.Initialize 方法。IShellExtInit.Initialize 向上下文菜单扩展提供一个 IDataObject 对象,该对象以 CF_HDROP 格式包含一个或多个文件名。您可以通过 IDataObject 对象枚举选定的文件和文件夹。如果从 IShellExtInit.Initialize 返回失败的 HRESULT (抛出异常),则不会使用上下文菜单扩展。

在代码示例中,FileContextMenuExt.Initialize 方法枚举选定的文件和文件夹。如果只选择了一个文件,该方法会将文件名存储起来供以后使用。如果选择了多个文件或未选择任何文件,该方法将抛出带有 E_FAIL HRESULT 的异常,以不使用上下文菜单扩展。

2. 实现 IContextMenu

IShellExtInit.Initialize 成功返回后,会调用 IContextMenu.QueryContextMenu 方法来获取上下文菜单扩展将添加的菜单项。QueryContextMenu 的实现相当直接。上下文菜单扩展使用 InsertMenuItem 或类似函数添加其菜单项。菜单命令标识符必须大于或等于 idCmdFirst 并且小于 idCmdLastQueryContextMenu 必须返回添加到菜单的最大数字标识符加一。分配菜单命令标识符的最佳方法是从零开始按顺序递增。如果上下文菜单扩展不需要向菜单添加任何项,它应该简单地从 QueryContextMenu 返回。

在此代码示例中,我们插入菜单项“Display File Name (C#)”并在其下方添加一个菜单分隔符。

调用 IContextMenu.GetCommandString 来检索菜单项的文本数据,例如为菜单项显示的帮助文本。如果用户突出显示了由上下文菜单处理程序添加的某个项,则会调用处理程序的 IContextMenu.GetCommandString 方法来请求将在 Windows 资源管理器状态栏显示的帮助文本字符串。此方法还可以用于请求分配给命令的动词字符串。可以请求 ANSI 或 Unicode 动词字符串。此示例仅实现了对 uFlags Unicode 值 else 的支持,因为自 Windows 2000 以来,Windows 资源管理器仅使用了这些值。

当选择了上下文菜单扩展添加的菜单项之一时,会调用 IContextMenu.InvokeCommand 。上下文菜单响应此方法执行或启动所需的动作。

将处理程序注册到特定文件类

上下文菜单处理程序与文件类或文件夹关联。对于文件类,处理程序在以下子项下注册。

HKEY_CLASSES_ROOT\<File Type>\shellex\ContextMenuHandlers

上下文菜单处理程序的注册在 FileContextMenuExt Register 方法中实现。附加到该方法的 ComRegisterFunction 属性使基本 COM 类的注册之外的用户编写代码得以执行。Register 调用 ShellExtLib.cs 中的 ShellExtReg.RegisterShellExtContextMenuHandler 方法将处理程序与特定文件类型关联。如果文件类型以 '.' 开头,它会尝试读取 HKCR\<File Type> 键的默认值,该值可能包含文件类型链接到的程序 ID。如果默认值不为空,则使用程序 ID 作为文件类型继续注册。

例如,此代码示例将处理程序与“.cs”文件关联。在安装 Visual Studio 2010 时,HKCR\.cs 默认的默认值为“VisualStudio.cs.10.0”,因此我们继续在 HKCR\VisualStudio.cs.10.0\ 下注册处理程序,而不是在 HKCR\.cs 下注册。在示例处理程序的注册过程中,会添加以下项和值。

    HKCR
    {
        NoRemove .cs = s 'VisualStudio.cs.10.0'
        NoRemove VisualStudio.cs.10.0
        {
            NoRemove shellex
            {
                NoRemove ContextMenuHandlers                
        {
                    
        {B1F1405D-94A1-4692-B72F-FC8CAF8B8700} = 
        s 'CSShellExtContextMenuHandler.FileContextMenuExt'                
        }
            }
        }
    }

注销在 FileContextMenuExt Unregister 方法中实现。与 Register 方法类似,附加到该方法的 ComUnregisterFunction 属性使在注销过程中执行用户编写的代码成为可能。它会移除 HKCR\<File Type>\shellex\ContextMenuHandlers 下的 {<CLSID>} 项。

下载

请访问 http://1code.codeplex.com 下载最新的代码示例。

© . All rights reserved.