• Windows Shell扩展系列文章 1 .NET 4 编写Windows Shell上下文菜单扩展


    示例代码下载

    在MSDN论坛,大量的开发人员经常问道这样一个问题:

    如何编写.NET代码开发Windows Shell扩展?

    在.NET Framework 4问世之前,使用.NET编写进程内的Windows Shell 扩展是不被支持的。开发人员不得不使用native C++进行编写。原因是.NET 4之前的CLR只允许一个版本的CLR运行在同一进程内。CLR项目经理Jesse Kapan在此论坛帖中对这个问题有详细阐述。

    随着.NET 4引入了CLR in-process side-by-side特性,使用.NET 4或未来更高版本.NET编写Windows Shell扩展变成了可能。在.NET 4中CLR支持下列情况的In-Proc SxS:

    1. v2.0和v4.0共存

    2. v1.1和v4.0共存

    而V1.1和V2.0则是不能够被同时加载到进程中。也就是说,进程中<4.0的CLR只能存在一个实例,这样做的原因非常简单:<4.0的CLR版本本身是不支持In-Proc SxS的,也就是说v1.1和v2.0一旦在同一个进程内加载是会出现各种各样的问题的。并且,我们不希望因为要支持SxS而去修改v1.1和v2.0,这样做的代价太大,同时也会把整个问题域变得更加复杂,因此最后决定不支持<4.0的CLR多于一个实例。当然了,>=4.0的CLR是可以多个并存的,也就是说V4.0,V5.0,v6.0,等等,都是可以和平共处在同一个进程内。原因很简单,>4.0的CLR是In-Proc SxS Aware的。

    解释了那么多理论的东西,那我究竟该如何编写.NET代码开发Windows Shell 扩展呢?

    本系列文章将逐一为你介绍使用.NET开发Windows Shell 上下文菜单扩展,Icon扩展,Drag-and-drop扩展,缩略图扩展,Icon overlay扩展等的详细开发方法和示例。

    从最常见的Windows Shell 上下文扩展开始说起。

    示例代码 (示例代码下载

    CSShellExtContextMenuHandler:   Shell context menu handler (C#)
    VBShellExtContextMenuHandler:   Shell context menu handler (VB.NET)
    CppShellExtContextMenuHandler: Shell context menu handler (C++)

    示例演示

    当你在Visual Studio 2010中成功编译CSShellExtContextMenuHandler示例,你会得到CSShellExtContextMenuHandler.dll。随后,如你的操作系统是x86的,编译并安装CSShellExtContextMenuHandlerSetup(x86)。如你的操作系统是x64的,编译并安装CSShellExtContextMenuHandlerSetup(x64)。

    安装完后,找到任何一个.cs文件。在Windows Explorer中右键该文件。你会看到 “Display File Name (C#)” 上下文菜单项。该菜单项就是由我们的上下文菜单扩展示例添加的。点击该菜单项,你会看到一个消息框显示该.cs文件的完整路径。

    image

    实现细节

    A. 创建和设置工程

    在Visual Studio 2010中, 创建一个名为"CSShellExtContextMenuHandler"的Visual C# / Windows / Class Library 工程, 在签名页上,使用强名称密钥文件对该程序集进行签名.


    -----------------------------------------------------------------------------

    B. 执行一个基本组件对象模型 (COM) DLL

    所有扩展应用程序都是以COM对象的方式在进程内运行.
    创建一个基本的.NET COM对象是很简单的事情.您只需要定义一个公共类该类的ComVisible属性设置为true,
    使用Guid属性设置一个CLSID,显式实现某些COM接口.例如,

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

    您甚至不需要自己实现 IUnknown 和类工厂,因为.net 框架已经为您处理好了.

    -----------------------------------------------------------------------------

    C. 使用上下文菜单应用程序并关联一个文件类型

    -----------
    上下文应用程序的实现:

    FileContextMenuExt.cs 文件定义上下文菜单处理程序. 必须继承IShellExtInit and IContextMenu
    接口. 在文件ShellExtLib.cs中你可以看到接口可以通过COMImport属性导入

        [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)
            {
                ...
            }
        }


    COM操作使得具有最终输出参数的函数看起来是由它返回的该值,PreserveSig属性用于关闭这一特性。 当您不设置(例如,GetCommandString 方法 PreserveSigAttributeIContextMenu)失败时候 ,将引发一个.NET 的异常。
    例如 Marshal.ThrowExceptionForHR(WinError.E_FAIL) ;在 PreserveSigAttribute 应用于托管的方法的签名时,属性化方法的托管和非托管签名是相同的 (例如
    QueryContextMenu 方法的 IContextMenu)。保留原始的方法,如果该成员返回多个成功的 HRESULT值签名是必要的,并且您想要检测不同的值。

    只有该上下文应用程序被成功注册了,当上下文菜单显示的时候才能被实例化.

      1. IShellExtInit接口实现

      当上下文扩展COM对象被实例化时,IShellExtInit::Initialize方法将被调用.
      IShellExtInit::Initialize  提供与在 CF_HDROP 格式中保存一个或多个文件名称的IDataObject 对象的上下文菜单扩展。所选的文件和文件夹通过 IDataObject 对象,您可以枚举。如果 IShellExtInit::Initialize 返回的是S_OK 以外的其他任何值则不能使用上下文菜单扩展.
     
      在示例代码中, FileContextMenuExt::Initialize 枚举被选中的文件和文件夹. 只有一个文件被选中的时候, 这个方法将保存文件名并返回S_OK供后续处理.  如果没有文件或不止一个文件被选中则函数返回E_FAIL您将不能使用这个上下文应用程序.


      2.  IContextMenu接口实现:

      当IShellExtInit::Initialize返回iS_OK后, IContextMenu::QueryContextMenu这个方法将被调用用以获取子菜单项或添加子菜单项.  QueryContextMenu方法的实现是非常简单的.上下文扩展使用InsertMenuItem或类是的函数插入子菜单项.   菜单标示符ID必须大于第一个菜单标示符ID且小于最后一个菜单标示符ID. QueryContextMenu必须返回可用的最大的标识符ID并加一的标识符ID. 指定菜单命令标识符的最佳方法是在序列中从0开始.如果上下文菜单扩展无需添加菜单项则QueryContextMenu返回0.
     
      在示例代码中, 我们插入一个 "Display File Name (C#)"子菜单项并在它的下面加一个分隔符.

      IContextMenu::GetCommandString方法被用来检索并返回子菜单项的文本, 例如,为菜单项显示帮助文本. 如果用户选中了上下文菜单中添加的子菜单,应用程序的IContextMenu::GetCommandString 方法将被调用来获取帮助文本并显示在资源管理器的状态栏上ANSI或Unicode的字符集都可以被使用. 示例程序只使用Unicode的uFlags参数, 因为自Windows 2000以后资源管理器只接受Unicode的字符集.

      当某个通过上下文菜单扩展安装的子菜单项被选中时,IContextMenu::InvokeCommand的方法在上下文菜单中执行或激发响应此方法所需的操作.

    -----------
    注册为某一类文件的处理程序:

    上下文菜单处理程序关联的类文件或文件夹.
    文件类, 程序将被注册在.

        HKEY_CLASSES_ROOT\<File Type>\shellex\ContextMenuHandlers

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

    例如, 示例文件关联了 '.cs' 类型的文件.
    如果您安装了Visual Studio 2010则注册表项HKCR\.cs下就有了一个默认的文件类型'VisualStudio.cs.10.0',所以将使用'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函数中被实现并执行,类似注册的方法, ComUnregisterFunction属性附加到该方法使基本以外的其他用户编写代码的执行
    注销的COM 类,执行后将删除注册表项HKCR\CLSID\{<CLSID>} 键 {<CLSID>}和HKCR\<File Type>\shellex\ContextMenuHandlers下的值.

    下载

    http://1code.codeplex.com/releases

    下载后,在Visual Studio 2010目录下找到CS/VB/CppShellExtContextMenuHandler 示例。

    如你有任何反馈或问题,欢迎通过onecode@microsoft.com联系我们。

  • 相关阅读:
    Chunky Monkey(算法)
    Confirm the Ending(算法)
    Return Largest Numbers in Arrays(算法)
    防止SQL注入总结
    mybatis中的#和$的区别 以及 防止sql注入
    拆分字符串为树形结构
    虚拟机类加载机制
    linux加载字体
    项目上线暴露出的问题
    浅析正则表达式-应用篇
  • 原文地址:https://www.cnblogs.com/Jialiang/p/ShellExtension1.html
Copyright © 2020-2023  润新知