Click here to Skip to main content
15,880,891 members
Articles / Programming Languages / Visual Basic

How to Write Windows Shell Extension with .NET Languages

Rate me:
Please Sign up or sign in to vote.
4.69/5 (27 votes)
29 Mar 2011Ms-PL8 min read 297.8K   13K   126   34
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:

C#
[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.

C#
[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.

C++
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)


Written By
China China
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

 
QuestionSeems to not work Pin
Member 1397726130-Jul-23 22:40
Member 1397726130-Jul-23 22:40 
QuestionThanks a lot! Pin
Member 21635887-Jan-18 19:51
Member 21635887-Jan-18 19:51 
AnswerRe: Thanks a lot! Pin
Member 243760623-May-20 11:18
Member 243760623-May-20 11:18 
QuestionActivate on multiple File Types Pin
Member 76868927-Apr-15 3:23
Member 76868927-Apr-15 3:23 
AnswerRe: Activate on multiple File Types Pin
Mayura Vivekananda2-May-15 23:19
Mayura Vivekananda2-May-15 23:19 
QuestionNOTE!!! Pin
Zeveloper18-Sep-14 9:05
Zeveloper18-Sep-14 9:05 
QuestionWindows 8.1 x64 Pin
Zeveloper17-Sep-14 11:52
Zeveloper17-Sep-14 11:52 
Questionabout GUIDs used in this project Pin
Member 1050000014-Jan-14 7:01
Member 1050000014-Jan-14 7:01 
AnswerRe: about GUIDs used in this project Pin
Yves Goergen1-Feb-16 22:27
Yves Goergen1-Feb-16 22:27 
QuestionMultiple files selected Pin
Luis Fernando Bilac26-Nov-13 2:31
Luis Fernando Bilac26-Nov-13 2:31 
AnswerRe: Multiple files selected Pin
Member 1186015827-Jul-15 3:11
Member 1186015827-Jul-15 3:11 
GeneralRe: Multiple files selected Pin
Member 1186015827-Jul-15 23:40
Member 1186015827-Jul-15 23:40 
GeneralMy vote of 5 Pin
tumbledDown2earth27-Feb-13 18:28
tumbledDown2earth27-Feb-13 18:28 
BugDuring upgrade the windows explorer.exe is getting killed Pin
Sathish Kumar Palanisamy7-Dec-12 5:23
Sathish Kumar Palanisamy7-Dec-12 5:23 
Answerx64 Windows 7 Solution Pin
MikeMatt164-Jun-12 19:16
MikeMatt164-Jun-12 19:16 
GeneralRe: x64 Windows 7 Solution Pin
blak3r7-Jan-13 15:23
blak3r7-Jan-13 15:23 
GeneralRe: x64 Windows 7 Solution Pin
Zeveloper17-Sep-14 11:45
Zeveloper17-Sep-14 11:45 
QuestionXP Pin
smelis24-May-12 1:36
smelis24-May-12 1:36 
QuestionUsable source code? Pin
Kamran Behzad17-Apr-12 21:33
Kamran Behzad17-Apr-12 21:33 
QuestionKill explorer.exe to to rebuild and deploy Pin
alsamflux18-Jan-12 15:37
alsamflux18-Jan-12 15:37 
QuestionI tried to change the extension to .JPG and recompiled. Pin
Bangon Kali31-Oct-11 8:24
Bangon Kali31-Oct-11 8:24 
GeneralRe: I tried to change the extension to .JPG and recompiled. Pin
Intelevgen17-Apr-12 23:29
Intelevgen17-Apr-12 23:29 
AnswerRe: I tried to change the extension to .JPG and recompiled. Pin
Neal17595-Nov-12 9:58
Neal17595-Nov-12 9:58 
QuestionAdding a column for file/path length in windows explorer Pin
Member 435848925-Aug-11 11:18
Member 435848925-Aug-11 11:18 
QuestionBad idea? Pin
rittjc4-Apr-11 13:57
rittjc4-Apr-11 13:57 
Hmmm...there is a potential problem here that can make a mess. I think it is a bad idea to use .NET to extend an application intra-process. What happens if there is another extension used by Explorer written in 2.0 and yours is 3.0+? If it loads before you, then you can't load the 3.0+ version. Shell Extensions are intra process for Explorer. If you load first, then no problem as there is backward compatibility, but if the other app loads first you cannot "up-version" the .NET assembly that is already loaded.

How does this solution avoid this potential problem of incompatibility with other running .NET based extensions?

Jim

I didn't rate this article. Its a good tutorial but there is a real versioning issue here as you don't control what other pieces are in place. Perhaps if you limit yourself to using .NET 2.0????

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

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