Click here to Skip to main content
Click here to Skip to main content

How to Write Windows Shell Extension with .NET Languages

, 29 Mar 2011
Rate this:
Please Sign up or sign in to vote.
The C# code sample demonstrates creating a Shell context menu handler with .NET Framework 4

About Us

The Microsoft All-In-One Code Framework (http://1code.codeplex.com) is a free, centralized code sample library driven by developers' needs. Our goal is to provide typical code samples for all Microsoft development technologies, and reduce developers' efforts in solving typical programming tasks.

Our team listens to developers’ pains in MSDN forums, social media and various developer communities. We write code samples based on developers’ frequently asked programming tasks, and allow developers to download them with a short code sample publishing cycle. Additionally, our team offers a free code sample request service. This service is a proactive way for our developer community to obtain code samples for certain programming tasks directly from Microsoft.

Introduction

In MSDN forums, lots of developers ask how to write Windows Shell extension with .NET languages (e.g. C#, VB.NET).

Prior to .NET Framework 4, the development of in-process shell extensions using managed code was not officially supported because of the CLR limitation allowing only one .NET runtime per process. Jesse Kaplan, one of the CLR program managers, explains it in this MSDN forum thread.

In .NET 4, with the ability to have multiple runtimes in process with any other runtime, Microsoft can now offer general support for writing managed shell extensions—even those that run in-process with arbitrary applications on the machine. This article introduces the in-process side-by-side feature in detail. However, please note that you still cannot write shell extensions using any version earlier than .NET Framework 4 because those versions of the runtime do not load in-process with one another and will cause failures in many cases.

The document explains the theory. How on earth can I write a managed shell extension?

If you search on the internet, you would find that there are almost zero .NET 4 shell extension samples. The few .NET 2 shell extension samples (not supported because of the above reason) have more or less some defects, e.g. not being able to load in x64 environment. In order to meet customers’ needs, we, All-In-One Code Framework project group, would like to fill in the blank. The project group has planned a series of .NET 4 managed Shell extension code samples for Context Menu Handler, Property Sheet Handler, Icon handler, Data handler, Drop handler, Drag-and-drop handler, Thumbnail Handler, Icon Handler, Icon Overlay Handler, and so on. This article introduces the first sample: Context Menu Handler.

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

Demo

Here is a quick demo of the context menu handler code sample. After you successfully build the sample project CSShellExtContextMenuHandler in Visual Studio 2010, you will get a DLL: CSShellExtContextMenuHandler.dll. Run 'Visual Studio Command Prompt (2010)' (or 'Visual Studio x64 Win64 Command Prompt (2010)' if you are on a x64 operating system) in the Microsoft Visual Studio 2010 \ Visual Studio Tools menu as administrator. Navigate to the folder that contains the build result CSShellExtContextMenuHandler.dll and enter the command:

Regasm.exe CSShellExtContextMenuHandler.dll /codebase

to register the context menu handler.

Find a .cs file in the Windows Explorer (e.g. FileContextMenuExt.cs in the sample folder), and right click it. You would see the "Display File Name (C#)" menu item in the context menu and a menu seperator below it. Clicking the menu item brings up a message box that displays the full path of the .cs file.

2262.untitled.jpg

Implementation Details

A. Creating and configuring the project

In Visual Studio 2010, create a Visual C# / Windows / Class Library project named "CSShellExtContextMenuHandler". Open the project properties, and in the Signing page, sign the assembly with a strong name key file.

B. Implementing a basic Component Object Model (COM) DLL

Shell extension handlers are all in-process COM objects implemented as DLLs. Making a basic .NET COM component is very straightforward. You just need to define a 'public' class with ComVisible(true), use the Guid attribute to specify its CLSID, and explicitly implement certain COM interfaces. For example:

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

You even do not need to implement IUnknown and class factory by yourself because .NET Framework handles them for you.

C. Implementing the context menu handler and registering it for a certain file class

Implementing the context menu handler:

The FileContextMenuExt.cs file defines a context menu handler. The context menu handler must implement the IShellExtInit and IContextMenu interfaces. The interfaces are imported using the COMImport attribute in 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)
        {
            ...
        }
    }

The PreserveSig attribute indicates that the HRESULT or retval signature transformation that takes place during COM interop calls should be suppressed. When you do not apply PreserveSigAttribute (e.g. the GetCommandString method of IContextMenu), the failure HRESULT of the method needs to be thrown as a .NET exception. For example, Marshal.ThrowExceptionForHR(WinError.E_FAIL); When you apply the PreserveSigAttribute to a managed method signature, the managed and unmanaged signatures of the attributed method are identical (e.g. the QueryContextMenu method of IContextMenu). Preserving the original method signature is necessary if the member returns more than one success HRESULT value and you want to detect the different values.

A context menu extension is instantiated when the user displays the context menu for an object of a class for which the context menu handler has been registered.

1 Implementing IShellExtInit

After the context menu extension COM object is instantiated, the IShellExtInit.Initialize method is called. IShellExtInit.Initialize supplies the context menu extension with an IDataObject object that holds one or more file names in CF_HDROP format. You can enumerate the selected files and folders through the IDataObject object. If a failure HRESULT is returned (thrown) from IShellExtInit.Initialize, the context menu extension will not be used.

In the code sample, the FileContextMenuExt.Initialize method enumerates the selected files and folders. If only one file is selected, the method stores the file name for later use. If more than one file or no file are selected, the method throws an exception with the E_FAIL HRESULT to not use the context menu extension.

2. Implementing IContextMenu

After IShellExtInit.Initialize returns successfully, the IContextMenu.QueryContextMenu method is called to obtain the menu item or items that the context menu extension will add. The QueryContextMenu implementation is fairly straightforward. The context menu extension adds its menu items using the InsertMenuItem or similar function. The menu command identifiers must be greater than or equal to idCmdFirst and must be less than idCmdLast. QueryContextMenu must return the greatest numeric identifier added to the menu plus one. The best way to assign menu command identifiers is to start at zero and work up in sequence. If the context menu extension does not need to add any items to the menu, it should simply return from QueryContextMenu.

In this code sample, we insert the menu item "Display File Name (C#)", and add a menu seperator below it.

IContextMenu.GetCommandString is called to retrieve textual data for the menu item, such as help text to be displayed for the menu item. If a user highlights one of the items added by the context menu handler, the handler's IContextMenu.GetCommandString method is called to request a Help text string that will be displayed on the Windows Explorer status bar. This method can also be called to request the verb string that is assigned to a command. Either ANSI or Unicode verb strings can be requested. This example only implements support for the Unicode values of uFlags, because only those have been used in Windows Explorer since Windows 2000.

IContextMenu.InvokeCommand is called when one of the menu items installed by the context menu extension is selected. The context menu performs or initiates the desired actions in response to this method.

Registering the handler for a certain file class:

Context menu handlers are associated with either a file class or a folder. For file classes, the handler is registered under the following subkey.

HKEY_CLASSES_ROOT\<File Type>\shellex\ContextMenuHandlers

The registration of the context menu handler is implemented in the Register method of FileContextMenuExt. The ComRegisterFunction attribute attached to the method enables the execution of user-written code other than the basic registration of the COM class. Register calls the ShellExtReg.RegisterShellExtContextMenuHandler method in ShellExtLib.cs to associate the handler with a certain file type. If the file type starts with '.', it tries to read the default value of the HKCR\<File Type> key which may contain the Program ID to which the file type is linked. If the default value is not empty, use the Program ID as the file type to proceed the registration.

For example, this code sample associates the handler with '.cs' files. HKCR\.cs has the default value 'VisualStudio.cs.10.0' by default when Visual Studio 2010 is installed, so we proceed to register the handler under HKCR\VisualStudio.cs.10.0\ instead of under HKCR\.cs. The following keys and values are added in the registration process of the sample handler.

    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'                
        }
            }
        }
    }

The unregistration is implemented in the Unregister method of FileContextMenuExt. Similar to the Register method, the ComUnregisterFunction attribute attached to the method enables the execution of user-written code during the unregistration process. It removes the {<CLSID>} key under HKCR\<File Type>\shellex\ContextMenuHandlers.

Download

Please visit http://1code.codeplex.com to download the latest code sample.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Microsoft All-In-One Code Framework delineates the framework and skeleton of Microsoft development techniques through typical sample codes in three popular programming languages (Visual C#, VB.NET, Visual C++). Each sample is elaborately selected, composed, and documented to demonstrate one frequently-asked, tested or used coding scenario based on our support experience in MSDN newsgroups and forums. If you are a software developer, you can fill the skeleton with blood, muscle and soul. If you are a software tester or a support engineer like us, you may extend the sample codes a little to fit your specific test scenario or refer your customer to this project if the customer's question coincides with what we collected.
http://cfx.codeplex.com/

Comments and Discussions

 
Questionabout GUIDs used in this project PinmemberMember 1050000014-Jan-14 7:01 
QuestionMultiple files selected PinmemberLuis Fernando Bilac26-Nov-13 2:31 
GeneralMy vote of 5 Pinmemberarnab_pal27-Feb-13 18:28 
BugDuring upgrade the windows explorer.exe is getting killed PinmemberSathish Kumar Palanisamy7-Dec-12 5:23 
Answerx64 Windows 7 Solution PinmemberMikeDaMan25944-Jun-12 19:16 
GeneralRe: x64 Windows 7 Solution Pinmemberblak3r7-Jan-13 15:23 
QuestionXP Pinmembersmelis24-May-12 1:36 
QuestionUsable source code? PinmemberKamran Behzad17-Apr-12 21:33 
QuestionKill explorer.exe to to rebuild and deploy Pinmemberalsamflux18-Jan-12 15:37 
QuestionI tried to change the extension to .JPG and recompiled. [modified] PinmemberBangon Kali31-Oct-11 8:24 
GeneralRe: I tried to change the extension to .JPG and recompiled. PinmemberIntelevgen17-Apr-12 23:29 
AnswerRe: I tried to change the extension to .JPG and recompiled. PinmemberNeal17595-Nov-12 9:58 
QuestionAdding a column for file/path length in windows explorer PinmemberMember 435848925-Aug-11 11:18 
QuestionBad idea? Pinmemberrittjc4-Apr-11 13:57 
AnswerRe: Bad idea? Pinmemberarchitecton5-Apr-11 2:53 
GeneralRe: Bad idea? Pinmemberrittjc5-Apr-11 12:39 
GeneralMy vote of 5 PinmemberMonjurul Habib1-Apr-11 8:26 
GeneralBrilliant! Five stars PinmemberAnt210031-Mar-11 7:15 
GeneralMy vote of 5 PinmemberKing_kLAx30-Mar-11 23:56 
Joke Pinmemberlongware29-Mar-11 16:54 
GeneralRe: 顶 PinmemberToli Cuturicu5-Apr-11 1:28 
GeneralRe: 顶 PinmemberDarchangel12-Apr-11 7:48 
GeneralRe: 顶 PingroupDaveGordon13-Feb-13 12:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 29 Mar 2011
Article Copyright 2011 by All-In-One Code Framework
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid