<p><a href="http://1code.codeplex.com/releases"><strong><span style="text-decoration: underline;"><span style="color: #3d81ee;">示例代码下载</span></span></strong></a>: <a href="http://1code.codeplex.com/releases/view/62253#DownloadId=215068"><strong>C#</strong></a><strong>, </strong><a href="http://1code.codeplex.com/releases/view/62253#DownloadId=215141"><strong>VB.NET</strong></a></p>
<p></p>
<p>在MSDN论坛,大量的开发人员经常问道这样一个问题:</p>
<blockquote>
<p><span style="font-size: small;">如何编写.NET代码开发Windows Shell扩展?</span></p>
</blockquote>
<ul>
<li><a href="http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ceb44d5-dce8-4197-ac55-f0f4fb59eeb4/"><span style="color: #3d81ee;">http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ceb44d5-dce8-4197-ac55-f0f4fb59eeb4/</span></a></li>
<li><a href="http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ce0c480-59e3-4732-a608-1974a908e44a/"><span style="color: #3d81ee;">http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ce0c480-59e3-4732-a608-1974a908e44a/</span></a></li>
<li><a href="http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/1428326d-7950-42b4-ad94-8e962124043e"><span style="color: #3d81ee;">http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/1428326d-7950-42b4-ad94-8e962124043e</span></a></li>
<li>
<a href="http://social.msdn.microsoft.com/Forums/en-US/clr/thread/63d04f72-5c71-40a9-aea3-519c9e9591a6"><span style="color: #3d81ee;">http://social.msdn.microsoft.com/Forums/en-US/clr/thread/63d04f72-5c71-40a9-aea3-519c9e9591a6</span></a> </li>
</ul>
<p>在.NET Framework 4问世之前,使用.NET编写进程内的Windows Shell 扩展是不被支持的。开发人员不得不使用native C++进行编写。原因是.NET 4之前的CLR只允许一个版本的CLR运行在同一进程内。CLR项目经理Jesse Kapan在<a href="http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/1428326d-7950-42b4-ad94-8e962124043e"><span style="color: #3d81ee;">此论坛帖</span></a>中对这个问题有详细阐述。</p>
<p></p>
<p>随着.NET 4引入了<a href="http://blog.csdn.net/silverlightshanghai/archive/2009/08/25/4482880.aspx"><span style="color: #3d81ee;">CLR in-process side-by-side特性</span></a>,使用.NET 4或未来更高版本.NET编写Windows Shell扩展变成了可能。在.NET 4中CLR支持下列情况的In-Proc SxS:</p>
<p>1. v2.0和v4.0共存</p>
<p>2. v1.1和v4.0共存</p>
<p>而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的。</p>
<p></p>
<p>解释了那么多理论的东西,那我究竟该如何编写.NET代码开发Windows Shell 扩展呢?</p>
<p>本系列文章将逐一为你介绍使用.NET开发Windows Shell 上下文菜单扩展,Icon扩展,Drag-and-drop扩展,缩略图扩展,Icon overlay扩展等的详细开发方法和示例。</p>
<p></p>
<p>从最常见的Windows Shell 上下文菜单扩展开始说起。</p>
<p></p>
<p>示例代码 (<a href="http://1code.codeplex.com/releases"><strong><span style="text-decoration: underline;"><span style="color: #3d81ee;">示例代码下载</span></span></strong></a>)</p>
<p><strong>CSShellExtContextMenuHandler</strong>: Shell context menu handler (C#) <br><strong>VBShellExtContextMenuHandler</strong>: Shell context menu handler (VB.NET)<br><strong>CppShellExtContextMenuHandler</strong>: Shell context menu handler (C++)</p>
<p></p>
<h2>示例演示</h2>
<p>当你在Visual Studio 2010中成功编译CSShellExtContextMenuHandler示例,你会得到CSShellExtContextMenuHandler.dll。随后,如你的操作系统是x86的,编译并安装CSShellExtContextMenuHandlerSetup(x86)。如你的操作系统是x64的,编译并安装CSShellExtContextMenuHandlerSetup(x64)。</p>
<p>安装完后,找到任何一个.cs文件。在Windows Explorer中右键该文件。你会看到 “Display File Name (C#)” 上下文菜单项。该菜单项就是由我们的上下文菜单扩展示例添加的。点击该菜单项,你会看到一个消息框显示该.cs文件的完整路径。</p>
<p></p>
<p><a href="http://images.cnblogs.com/cnblogs_com/Jialiang/201103/201103230936529589.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/Jialiang/201103/201103230936556816.png" border="0" alt="image" width="400" height="188"></a></p>
<p></p>
<h2>实现细节</h2>
<p>A. 创建和设置工程</p>
<p>在Visual Studio 2010中, 创建一个名为"CSShellExtContextMenuHandler"的Visual C# / Windows / Class Library 工程, 在签名页上,使用强名称密钥文件对该程序集进行签名.</p>
<p><br>-----------------------------------------------------------------------------</p>
<p>B. 执行一个基本组件对象模型 (COM) DLL</p>
<p>所有扩展应用程序都是以COM对象的方式在进程内运行.<br>创建一个基本的.NET COM对象是很简单的事情.您只需要定义一个公共类该类的ComVisible属性设置为true,<br>使用Guid属性设置一个CLSID,显式实现某些COM接口.例如,</p>
<p>[ClassInterface(ClassInterfaceType.None)]<br>[Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)]<br>public class SimpleObject : ISimpleObject<br>{<br> ... // 实现接口<br>}</p>
<p>您甚至不需要自己实现 IUnknown 和类工厂,因为.net 框架已经为您处理好了.</p>
<p>-----------------------------------------------------------------------------</p>
<p>C. 使用上下文菜单应用程序并关联一个文件类型</p>
<p>-----------<br>上下文应用程序的实现:</p>
<p>FileContextMenuExt.cs 文件定义上下文菜单处理程序. 必须继承IShellExtInit and IContextMenu 接口. 在文件ShellExtLib.cs中你可以看到接口可以通过COMImport属性导入</p>
<p>[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]<br>[Guid("000214e8-0000-0000-c000-000000000046")]<br>internal interface IShellExtInit<br>{<br> void Initialize(<br> IntPtr pidlFolder,<br> IntPtr pDataObj,<br> IntPtr /*HKEY*/ hKeyProgID);<br>}</p>
<p>[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]<br>[Guid("000214e4-0000-0000-c000-000000000046")]<br>internal interface IContextMenu<br>{<br> [PreserveSig]<br> int QueryContextMenu(<br> IntPtr /*HMENU*/ hMenu,<br>uint iMenu,<br> uint idCmdFirst,<br> uint idCmdLast,<br> uint uFlags);</p>
<p> void InvokeCommand(IntPtr pici);</p>
<p> void GetCommandString(<br> UIntPtr idCmd,<br> uint uFlags,<br> IntPtr pReserved,<br> StringBuilder pszName,<br> uint cchMax);<br>}</p>
<p>[ClassInterface(ClassInterfaceType.None)]<br>[Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)]<br>public class FileContextMenuExt : IShellExtInit, IContextMenu<br>{<br>public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID)<br> {<br> ...<br> }<br><br> public int QueryContextMenu(<br> IntPtr hMenu,<br> uint iMenu,<br> uint idCmdFirst,<br> uint idCmdLast,<br> uint uFlags)<br> {<br> ...<br> }</p>
<p> public void InvokeCommand(IntPtr pici)<br> {<br> ...<br> }</p>
<p> public void GetCommandString(<br> UIntPtr idCmd,<br> uint uFlags,<br> IntPtr pReserved,<br> StringBuilder pszName,<br> uint cchMax)<br> {<br> ...<br> }<br>}</p>
<p><br>COM操作使得具有最终输出参数的函数看起来是由它返回的该值,PreserveSig属性用于关闭这一特性。 当您不设置(例如,GetCommandString 方法 PreserveSigAttributeIContextMenu)失败时候 ,将引发一个.NET 的异常。<br>例如 Marshal.ThrowExceptionForHR(WinError.E_FAIL) ;在 PreserveSigAttribute 应用于托管的方法的签名时,属性化方法的托管和非托管签名是相同的 (例如QueryContextMenu 方法的 IContextMenu)。保留原始的方法,如果该成员返回多个成功的 HRESULT值签名是必要的,并且您想要检测不同的值。</p>
<p>只有该上下文应用程序被成功注册了,当上下文菜单显示的时候才能被实例化.</p>
<p>1. IShellExtInit接口实现</p>
<p>当上下文扩展COM对象被实例化时,IShellExtInit::Initialize方法将被调用. <br>IShellExtInit::Initialize 提供与在 CF_HDROP 格式中保存一个或多个文件名称的IDataObject 对象的上下文菜单扩展。所选的文件和文件夹通过 IDataObject 对象,您可以枚举。如果 IShellExtInit::Initialize 返回的是S_OK 以外的其他任何值则不能使用上下文菜单扩展.<br><br>在示例代码中, FileContextMenuExt::Initialize 枚举被选中的文件和文件夹. 只有一个文件被选中的时候, 这个方法将保存文件名并返回S_OK供后续处理. 如果没有文件或不止一个文件被选中则函数返回E_FAIL您将不能使用这个上下文应用程序.</p>
<p><br>2. IContextMenu接口实现:</p>
<p>当IShellExtInit::Initialize返回iS_OK后, IContextMenu::QueryContextMenu这个方法将被调用用以获取子菜单项或添加子菜单项. QueryContextMenu方法的实现是非常简单的.上下文扩展使用InsertMenuItem或类是的函数插入子菜单项. 菜单标示符ID必须大于第一个菜单标示符ID且小于最后一个菜单标示符ID. QueryContextMenu必须返回可用的最大的标识符ID并加一的标识符ID. 指定菜单命令标识符的最佳方法是在序列中从0开始.如果上下文菜单扩展无需添加菜单项则QueryContextMenu返回0.<br><br>在示例代码中, 我们插入一个 "Display File Name (C#)"子菜单项并在它的下面加一个分隔符.</p>
<p>IContextMenu::GetCommandString方法被用来检索并返回子菜单项的文本, 例如,为菜单项显示帮助文本. 如果用户选中了上下文菜单中添加的子菜单,应用程序的IContextMenu::GetCommandString 方法将被调用来获取帮助文本并显示在资源管理器的状态栏上ANSI或Unicode的字符集都可以被使用. 示例程序只使用Unicode的uFlags参数, 因为自Windows 2000以后资源管理器只接受Unicode的字符集. 当某个通过上下文菜单扩展安装的子菜单项被选中时,IContextMenu::InvokeCommand的方法在上下文菜单中执行或激发响应此方法所需的操作.</p>
<p>-----------<br>注册为某一类文件的处理程序:</p>
<p>上下文菜单处理程序关联的类文件或文件夹. <br>对于文件类, 程序将被注册在.</p>
<p>HKEY_CLASSES_ROOT/<File Type>/shellex/ContextMenuHandlers</p>
<p></p>
<p>本上下文菜单处理程序的注册是在FileContextMenuExt方法中实现.ComRegisterFunction属性附加到该方法使基本以外的其他用户编写代码的执行注册的 COM 类。注册调用,ShellExtReg.RegisterShellExtContextMenuHandler 方法中,ShellExtLib.cs<br>将该处理程序与特定文件类型相关联。如果文件的类型是以'.'开头的,它会尝试读取 HKCR/ < 文件类型 > 键的可能的默认值包含链接的文件类型的程序 ID。如果默认值不为空,使用作为文件类型的程序 ID 进行注册。</p>
<p>例如, 示例文件关联了 '.cs' 类型的文件. <br>如果您安装了Visual Studio 2010则注册表项HKCR/.cs下就有了一个默认的文件类型'VisualStudio.cs.10.0',所以将使用'VisualStudio.cs.10.0'来取代HKCR/.cs下的文件类型.</p>
<p>HKCR<br>{<br> NoRemove .cs = s 'VisualStudio.cs.10.0'<br> NoRemove VisualStudio.cs.10.0<br> {<br> NoRemove shellex<br> {<br> NoRemove ContextMenuHandlers<br> {<br> {B1F1405D-94A1-4692-B72F-FC8CAF8B8700} = <br> s 'CSShellExtContextMenuHandler.FileContextMenuExt'<br> }<br> }<br> }<br>}</p>
<p>注销的动作将在FileContextMenuExt函数中被实现并执行,类似注册的方法, ComUnregisterFunction属性附加到该方法使基本以外的其他用户编写代码的执行注销的COM 类,执行后将删除注册表项HKCR/CLSID/{<CLSID>} 键 {<CLSID>}和HKCR/<File Type>/shellex/ContextMenuHandlers下的值.</p>
<h2>下载</h2>
<p><a title="http://1code.codeplex.com/releases" href="http://1code.codeplex.com/releases"><span style="color: #3d81ee;">http://1code.codeplex.com/releases</span></a></p>
<p>下载后,在Visual Studio 2010目录下找到CS/VB/CppShellExtContextMenuHandler 示例。</p>
<p></p>
<p>如你有任何反馈或问题,欢迎通过<a href="mailto:
[email protected]"><span style="color: #3d81ee;">
[email protected]</span></a>联系我们。</p>