Introduction

Until .NET 4.0, it was impossible to reliably create Windows Shell Extensions using .NET code. With improvements to the framework, it is now possible to use .NET to create these extensions. In this article, I'll show you how to quickly create Shell Context Menus as a C# class library.

Above: A screenshot of an example Shell Context Menu Extension - the 'Count Lines...' item is a custom shell extension that this article demonstrates how to create.

The Series

This article is part of the series '.NET Shell Extensions', which includes:

What are Shell Context Menus?

Shell Context Menus are COM servers that are registered in the system that allow the context menus of shell objects to be extended. This could be the context menu for a specific file type, such as *.txt files, file classes such as 'text files', drives, folders and more. The context menus can be used to provide advanced functionality that can be accessed quickly through Windows Explorer.

Getting Started

There's a lot of work involved in setting up Shell Extensions. Specific COM interfaces have to be implemented, servers must be built, the registry must be updated in a variety of ways. We're going to use a library I have developed called 'SharpShell' to do all of the hard work - leaving us with the task of creating a lightweight class library that contains our extension class.

Our Goal

The code below creates a Shell Extension that allows you to count the lines in any text file by right clicking on it and choosing 'Count Lines'. For the rest of the article, I'll show you how to create a library like this. The code is shown first because I want to highlight how straightforward writing these libraries is when using SharpShell.

Use Nuget

Use CodePlex

Rather than getting the library from this page, which may not be the latest version, you can always get the very latest version of the library from CodePlex - on the SharpShell home page which is https://sharpshell.codeplex.com. Nuget will always have the latest stable version - CodePlex may have betas available, and the CodeProject articles will have the version that was available at the time of writing.

Step 3: Deriving from SharpContextMenu

Now things get interesting. Derive your CountLinesExtension class from SharpContextMenu:

By implementing these two functions, we can provide all of the functionality needed. Here's what they do:

CanShowMenu

This function is called to determine whether we should show the Context Menu Extension for a given set of files. The files the user has selected are in the property SelectedItemPaths. We can check these file paths to see whether we actually want to show the menu. If the menu should be shown, return true. If not, return false.

CreateMenu

This function is called to actually create the Context Menu. A standard WinForms ContextMenuStrip is all we need to return.

For CanShowMenu, we return true always - shortly, we'll see why we don't need to validate that we have text files. For CreateMenu, we build a context menu strip with one item only, that has the caption 'Count Lines' and calls the CountLines function.

The CountLines function goes through the SelectedItemPaths and counts the lines in each file - then displays a message box with the summary.

Step 4: Handling the COM Registration

There are just a few things left to do. First, we must add the COMVisible attribute to our class.

[ComVisible(true)]
publicclass CountLinesExtension : SharpContextMenu

Why? Well, even though our class doesn't really look like it, it is in fact a COM server. If you were to look at the base class, you'd see that we're implementing COM interfaces such as IShellExtInit, IContextMenu, and ISharpShellServer. We don't need to worry about what they do, but for the system to be able to create our extension, it must have this attribute.

Next, we must give the assembly a strong name. There are ways around this requirement, but generally this is the best approach to take. To do this, right click on the project and choose 'Properties'. Then go to 'Signing'. Choose 'Sign the Assembly', specify 'New' for the key and choose a key name. You can password protect the key if you want to, but it is not required:

The final step - we now need to associate our extension with some file types. We can do that with the COMServerAssociation attribute (provided by SharpShell):

So what have we done here? We've told SharpShell that when registering the server, we want it to be associated with the class of *.txt files. This means that we won't just have it available for anything that ends in *.txt, but anything that is the same class. In basic terms, that's most things that share the same icon as the *.txt files.

You can do some pretty funky things with the COMServerAssociation attribute - you can associate with folders, drives, unknown files, specific extensions and so on. The full documentation for this feature is at COM Server Associations on the SharpShell CodePlex site.

And that's it! Building the project creates the CountLinesExtension assembly, which can be registered as a COM server to add the context menu to the system. Registering the COM server is a debugging and deployment task, so we'll talk about this in detail in the next section.

Debugging the Shell Extension

The Shell Extension is going to be hosted in Windows Explorer - due to the roundabout way that .NET COM Servers are loaded, it's damn near impossible to get a debugger into the process and step through the managed code. However, there is a way to debug your extension quickly. SharpShell comes with some tools that make working with SharpShell COM servers a bit easier, and one of them is the Server Manager. We can use this tool to debug our extension.

Open the Server Manager tool and use File > Load Server to load the built server file (the DLL). You can also drag the server into the main window. Selecting the server will show you some details on it.

The Server Manager is very useful - it will tell you whether the server is installed (32 or 64 bit mode) and some more details.

If you load the SharpContextMenu server, then select it, you can go to the 'Tools' menu and choose 'Test Context Menu'.

When you use 'Test Context Menu', you'll get a Test Shell Window. This is a basic implementation of the Windows Explorer application. You can right click on any item to test the Shell Context Menu.

Tip: Regardless of the COMServerAssocations you've set, the Test Shell will always attempt to create the context menu for the items created.

Attaching a debugger to the ServerManager.exe process will allow you to debug into your Context Menu and test its functionality, without having to register the server in Windows. Here's what the Test Shell will look like when running the Count Line Context menu extension.

Installing and Registering the Shell Extension

There are a number of ways to install and register SharpShell Shell Extensions. In this section, I'll detail them all.

The regasm Tool

You can use the tool 'regasm' to install and register a shell extension. When using regasm, the shell extension will be installed into the registry (i.e., the Class ID of the COM Server will be put in the COM Server Classes section and associated with the path to the actual server file), it will also register the associations.

The Server Manager Tool

The Server Manager Tool is my preferred approach for installing/uninstalling and registering/unregistering, at least during development, because it lets you install and register as separate steps. It will also let you specify whether you're installing/uninstalling etc in 32 bit or 64 bit mode.

Manual Registry Manipulation

Generally a bad approach, but if you absolutely have to then the MSDN documentation for Shell Extensions describes the changes that must be made to the registry to manually register a COM server, or a Managed COM Server. The documentation is listed in the 'Useful Resources' section.

Useful Resources

SharpShell on CodePlex: The home of the SharpShell project - includes documentation, discussions and the latest source code and releases

What's Next?

SharpShell will over time provide a mechanism to create all of the available Shell Extensions using .NET. Currently, Icon Handlers are implemented (I'm working on the documentation) and Property Sheet Handlers are implemented (with a few bugs to iron out). Each extension type will have an article.

I hope you've found this article useful - for feature requests, bugs or comments, you can use the comments section below or the CodePlex site.

Comments and Discussions

Yes you'll need to register your file type first - this might actually be a nice feature for a utility in sharpshell, some kind of class to allow you to register file types. Anyway, try this as a guide:

It seems that it doesn't work completely for shortcuts, it works perfectly with files and directories, but not with shortcuts
It adds new items to context menu well, but when I click on some new added item it doesn't invoke command that should be performed.
I've made small investigation and noticed one thing (I hope, it will help you a little bit ) :
when I click on new added item in the context menu in the function 'IContextMenu.InvokeCommand(IntPtr pici)'(SharpContextMenu.cs file) variable 'iciex' in fields 'verb' and 'verbW' has too big numbers like 236255072 or 286797496 and then it can not match this click to the appropriate command.

I can add my menu item to the context menu of particular file types with no problems. So the user is now able to select multiple files around the system without any copying starting yet. The next thing they need to do is provide the new location of the files. I therefore need to associate with the context menu of 'no files selected' - i.e. the menu that is displayed in the right hand explorer window with no file selections active.

I managed to successfully run this demo application, such that the extension is installed, registered and finally shows the corresponding "Count lines" context menu entry on text files. However, I need to be able to show a context menu entry regardless of the file type, wherefore I chose to modify the COMServerAssociation attribute to [COMServerAssociation(AssociationType.AllFiles)]. Sadly, this does not have any recognizable effect. Building and modifiying the registry works. I used the ServerManager and the regasm tool (and yes, I choose the x64 version ). Any ideas what I could try to apply the extension to all files?

Further informations:

I had to change

[COMServerAssociation(AssociationType.ClassOfExtension, ".txt")]

to

[COMServerAssociation(AssociationType.FileExtension, "textfile")]

for the context menu entry to show up.

- AssociationType.ClassOfExtension did not work in my case, no context menu entry.

- ".txt" did not work as well, I had to take the value of the registry entry HKEY_CURRENT_USER.Software.Microsoft.Windows.CurrentVersion.Explorer.FileExts..txt.UserChoice.ProgId, which in my case is "textfile".

I don't know if this is important or not, but I was astonished that I had to modify this code line at all as it obviously worked like that for many others.

That's very strange indeed - I can only assume that for some reason your registry classes have got into a slightly different state to that of most people's - hopefully what you've posted will help others out who get into the same situation!

That sounds strange - I haven't seen this behaviour before so it might be something to do with your setup. I would suggest posting an issue on the the github or codeplex page, then I can keep track of it and look into it for the next release

Hi,
How can I register that for current user only without admin privilegies? srm.exe unable to do that. It failed to register without admin privilegies. Dropbox does that for its context menu shell extension.

I'd have to investigate the code but I'm assuming that if you have issues with priviledges, it will be because it's registering for all users rather than the current user. You will either have to run as an admin or modify the code to allow user level registration - if you raise an issue on the repo at https://github.com/dwmkerr/sharpshell[^] then I can do it in the main codebase

Hi, I'm out of ideas, so if you could shed some light, it would be great.

I'm able to create the DLL, and test it using the ServerManager from SharpShellTools (got it using NuGet). It just doesn't show up in the actual Windows Explorer.

I'm currently using Win7 64, I tried installing with both ServerManager and regasm, with both architectures. I also tried generate the regfile and install from there, but no dice. The registry data is (apparently) inserted correctly when I check the regedit. The regasm doesn't give me any errors (it says it was installed successfully and I can cleanly unregister the data), but the link on the Context Menu is just not there

Thanks for the reply, Dave. I drilled down the code and discovered that this strange behavior has something to do with logging. When I override the Log method, it starts to work like a charm! No restart needed.

Sorry ,but I have another question.I want to show my contextmenus when I clik on directorys or any kinds of files(.rar,.txt,.docx......),Now I can just show on files or directorys,just one of them,I can't show on them all.

Sorry,but I've tried that,I used like this
[ComVisible(true)]
[COMServerAssociation(AssociationType.AllFiles)]
,then,It worked just in debug window,in that window,my contextmenu can display on files and directorys,but,in the really
window,it was not worked well,that is ,my menu can not display on any folders,it can only display on all files,like .txt.doc.jpg.rar....that's why please?