Introduction

Shell Icon Handlers are DLLs that are registered in the system to customise the appearance of icons. In this article I will show you how to create an Icon Handler extension using .NET and a library called SharpShell.

Above: An example Icon Handler Extension in action. This extension changes the icons for dll files to indicate whether they are Assemblies or Native DLLs.

The Series

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

Our Goal

To show how SharpShell and Shell Icon Handlers work we're going to create a DLL that changes the icons of *.dll files - showing them in one colour for standard DLLs and another colour for Assemblies. Just to show how easily this can be done with SharpShell, here's the final class:

///<summary>/// The DllIconHandler is a Shell Icon Handler exception that
/// shows different icons for native and managed dlls.
///</summary>[ComVisible(true)]
[COMServerAssocation(AssociationType.ClassOfExtension, ".dll")]
publicclass DllIconHandler : SharpIconHandler
{
///<summary>/// Gets the icon.
///</summary>///<paramname="smallIcon">if set to <c>true</c> provide a small icon.</param>///<paramname="iconSize">Size of the icon.</param>///<returns>/// The icon for the file.
///</returns>protectedoverride Icon GetIcon(bool smallIcon, uint iconSize)
{
// The icon we'll return.
Icon icon = null;
// Check the assembly name. If it's a native dll, this'll throw an exception.
try
{
// SelectedItemPath is provided by 'SharpIconHandlder' and contains the path of the file.
AssemblyName.GetAssemblyName(SelectedItemPath);
}
catch (BadImageFormatException)
{
// The file is not an assembly.
icon = Properties.Resources.NativeDll;
}
catch (Exception)
{
// Some other eception occured, so assume we're native.
icon = Properties.Resources.NativeDll;
}
// If we haven't determined that the dll is native, use the managed icon.
if (icon == null)
icon = Properties.Resources.ManagedDll;
// Return the icon with the correct size. Use the SharpIconHandler 'GetIconSpecificSize'
// function to extract the icon of the required size.
return GetIconSpecificSize(icon, new Size((int)iconSize, (int)iconSize));
}
}

Can't get much easier than that. Now we'll look at how to create an Icon Handler Extension in detail.

Step 1: Creating the Project

First, create a new C# Class Library project.

Tip: You can use Visual Basic rather than C# - in this article the source code is C# but the method for creating a Visual Basic Shell Extension is just the same.

In this example we'll call the project 'DllIconHandler'. Rename the 'Class1.cs' file to 'DllIconHandler.cs'.

Now add the following references:

System.Windows.Forms

System.Drawing

System.Windows.Forms is needed because the SharpShell library depends on it (for things like context menus). System.Drawing is needed as we're going to want to use Icons.

Tip: If you use Nuget to install SharpShell (see 'Step 2') you don't need to add these references - they'll be added automatically.

Step 2: Referencing SharpShell

We now need to add a reference to the core SharpShell library. You can do that in a few different ways:

Add Reference

Download the 'SharpShell Library' zip file at the top of the article and add a reference to the downloaded SharpShell.dll file.

Tip: The download on this article is correct at the time of writing - if you need the latest version, use Nuget (as described below) or get the library from sharpshell.codeplex.com.

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 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 SharpIconHandler

Now we're actually going to create the functionality for the DLL Icon Handler. Derive your DllIconHandler class from SharpIconHandler:

Now we must implement the abstract members of the class. Right click on the SharpIconHandler part of the line and choose 'Implement Abstract Class'.

This'll create the implementation of the function we use to get the icon - needed - GetIcon.

///<summary>/// Gets the icon.
///</summary>///<paramname="smallIcon">if set to <c>true</c> provide a small icon.</param>///<paramname="iconSize">Size of the icon.</param>///<returns>/// The icon for the file.
///</returns>protectedoverride Icon GetIcon(bool smallIcon, uint iconSize)
{
// The icon we'll return.
Icon icon = null;
// Check the assembly name. If it's a native dll, this'll throw an exception.
try
{
// SelectedItemPath is provided by 'SharpIconHandlder' and contains the path of the file.
AssemblyName.GetAssemblyName(SelectedItemPath);
}
catch (BadImageFormatException)
{
// The file is not an assembly.
icon = Properties.Resources.NativeDll;
}
catch (Exception)
{
// Some other eception occured, so assume we're native.
icon = Properties.Resources.NativeDll;
}
// If we haven't determined that the dll is native, use the managed icon.
if (icon == null)
icon = Properties.Resources.ManagedDll;
// Return the icon with the correct size. Use the SharpIconHandler 'GetIconSpecificSize'
// function to extract the icon of the required size.
return GetIconSpecificSize(icon, new Size((int)iconSize, (int)iconSize));
}

GetIcon is called to get an Icon object for the selected file. The selected file (or folder, drive etc) path is stored in the SelectedItemPath property.

smallIcon is set to true if the shell is explicitly asking for a small icon. Generally we can ignore this, because the desired icon size is also provided, as iconSize.

A helper function GetIconSpecificSize is in the base class, this will return the correct sized icon from the Icon object - if this function is not used, then the system will get the first icon and resize it itself, normally this is not what you want.

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 DllIconHandler : SharpIconHandler

This is because even though SharpShell is hiding away most of the COM plumbing, its this class itself which is actually the COM Server - so it must be COM visible.

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 project 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:

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 *.dll files. This means that we won't just have it available for anything that ends in *.dll, but anything that is the same class. In basic terms that's most things that share the same icon as the *.dll files.

You can associate with files, folders, classes, drives and more - full documentation on using the association attribute is available on the CodePlex site at COM Server Associations.

And that's it! Building the project creates the DllIconHandler assembly, which can be registered as a COM server to add the icon handler system, providing coloured icons for dll files.

Debugging the Shell Extension

If you have seen the article .NET Shell Extensions - Context Menus you may recognise the 'Server Manager' tool. This is a tool in the SharpShell source code that can be used to help debug Shell Extensions.

Tip: If you want the latest version of the tools, they're available pre-built from the CodePlex site.

Open the Sever Manager tool and use File > Load Server to load the DllIconHandler.dll file. You can also drag the server into the main window. Selecting the server will show you some details on it. Select the server.

Now press 'Test Server' or use 'Server > Test...'. This will open the Test Shell which will simulate the calls that will be made to the server, just as if the Windows Shell was making the calls - however, as this is a managed application you can quickly attach a debugger to it and see how your server is running. It also lets you test the server without having to install or register it in the system, and this will save you a lot of time (when testing in Explorer for real, you'll be having to restart it lots to unlock the file so you can update it).

When testing Shell Icon Handler extensions, be aware of the following points:

Only shell items that match the COM Server Associations specified on the server will be tested.

Only the list view on the right will test the icons - the folders view on the left will use the standard shell icon.

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. So far, fully supported are Context Menu Extensions, Icon Handlers and Info Tip Handlers - follow the CodePlex project to stay up to date as new features are added.