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

.NET Shell Extensions - Shell Icon Handlers

By , 22 Feb 2013
 

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:

  1. .NET Shell Extensions - Shell Context Menus  
  2. .NET Shell Extensions - Shell Icon Handlers  
  3. .NET Shell Extensions - Shell Info Tip Handlers     
  4. .NET Shell Extensions - Shell Drop Handlers   
  5. .NET Shell Extensions - Shell Preview Handlers   
  6. .NET Shell Extensions - Shell Icon Overlay Handlers
  7. .NET Shell Extensions - Shell Thumbnail Handlers 

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")]
public class DllIconHandler : SharpIconHandler
{
    /// <summary>
    /// Gets the icon.
    /// </summary>
    /// <param name="smallIcon">if set to <c>true</c> provide a small icon.</param>
    /// <param name="iconSize">Size of the icon.</param>
    /// <returns>
    /// The icon for the file.
    /// </returns>
    protected override 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
        {
            //  FilePath is provided by 'SharpIconHandlder' and contains the path of the file.
            AssemblyName.GetAssemblyName(FilePath);
        }
        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: 

  1. System.Windows.Forms
  2. 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.  

Use Nuget

If you have Nuget installed, just do a quick search for SharpShell and install it directly - or get the package details at https://www.nuget.org/packages/SharpShell

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 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:
/// <summary>
/// The DllIconHandler is a Shell Icon Handler exception that
/// shows different icons for native and managed dlls.
/// </summary>
public class DllIconHandler : 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>
/// <param name="smallIcon">if set to <c>true</c> provide a small icon.</param>
/// <param name="iconSize">Size of the icon.</param>
/// <returns>
/// The icon for the file.
/// </returns>
protected override 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)]
public class 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:

[ComVisible(true)]
[COMServerAssocation(AssociationType.ClassOfExtension, ".dll")]
public class DllIconHandler : SharpIconHandler 

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: 

  1. Only shell items that match the COM Server Associations specified on the server will be tested.
  2. Only the list view on the right will test the icons - the folders view on the left will use the standard shell icon.   

Installing and Registering the Shell Extension 

You can check the 'Installing and Registering the Shell Extension' section of the .NET Shell Extensions - Shell Context Menus for details on how to install and register these extensions - the process is the same. 

Useful Resources   

How to Create Icon Handlers: The hub of the MSDN resources on shell icon handlers. Note that these resources are all for C and C++. 

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. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dave Kerr
Software Developer
United Kingdom United Kingdom
Follow my blog at www.dwmkerr.com and find out about my charity at www.childrenshomesnepal.org.
Follow on   Twitter

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDon'tmemberSteveKing24-Feb-13 23:02 
Says Raymond Chen[^]
QuestionWindows iconsmemberMayzz8-Feb-13 5:12 
Hello,
 
I have another question regarding Windows icons. How can we get the real icon associated with a file?
 
If it is a handler, retrieve the icon provided by it or if it is a file icon or icon in a dll, retrieve the entire file (containing all 32-bit formats like 32x32, 16x16 32-bit 32x32 24-bit, 24-bit 16x16 ...)
 
I tried with ExtarctAssociatedIcon but it does not leave the choice of icon, size (just large or small), but for example I can not choose 64x64 32-bit if the icon is provided by the program (like word for example).
 
Do you have a clue?
 
In any case, thank you again for this article and its sources. It is very enriching.
AnswerRe: Windows iconsmvpDave Kerr8-Feb-13 23:19 
For extracting the icons of a file, try taking at look at the article below:
 
Extract icons from EXE or DLL files[^]

GeneralRe: Windows iconsmemberMayzz9-Feb-13 2:16 
Thank you for this link.
 
I'm sorry to bother you again. In fact I am trying to retrieve the real icon associated with a file.
 
Unfortunately the Windows API (ExtractIcon, ExtarctAssociatedIcon) return a 16x16 or 32x32 icon but not return the real icons as we can see in Windows Explorer (like 128x128 32 bits for example).
 
To my knowledge, there is no API to do this. The only way is to look in then registry at HKEY_CLASS_ROOT for each file extension. The problem is that programs are not necessarily a path to a file containing icons in resource (DefaultIcon Key). It also gives the GUID to IconHandlers. think you there is a way to consume these IconHandler and extract the icon that it provide?
 
Thank you again
GeneralRe: Windows iconsmvpDave Kerr9-Feb-13 2:32 
OK I think that I understand what you want, tell me if I understand this right:
 
1. You want to be able to find the icons associated with an extension or class.
2. If the icon is defined via a 'defaulticon' key you can get it - because typically it points to a resource in dll/exe file.
3. Otherwise, you cannot get it.
 
As you mentioned, if class uses an Icon Handler, then in theory you can use this to get the icon, but you are not sure how.
 
Is this correct? If that is the case, then I can show you how to use SharpShell to use the IconHandler to get the icons. Smile | :)

GeneralRe: Windows iconsmemberMayzz9-Feb-13 4:28 
Yes. I would like to extract the icon provided by the Handler (sorry if I do not speak English very well).
 
With Server Manager tools, you can see that the icon extracted from an SharpShell Icon Handler. The only problem is that for loading a server it must inherits SharpIconHandler.
 
I then thought that SharpShell was able to load only its own classes to display the icon and not the other icons produced by other independent Icon Handler.
GeneralRe: Windows iconsmvpDave Kerr10-Feb-13 0:29 
No problems - because SharpShell has the definition of IExtractIcon, you can do the following - just make sure you provide the CLSID of the COM server Smile | :)
 
//  This is the guid of the server.
            var guid = new Guid("E1FD4E77-401D-46FA-A229-418EAB9AC4D3");
 
            //  Get the type for the guid.
            var type = Type.GetTypeFromCLSID(guid);
 
            //  Create the COM interface.
            var extractIcon = (IExtractIcon)Activator.CreateInstance(type);
 
            //  Get the icon location.
            var builder = new StringBuilder(260, 260);
            GILOutFlags flags;
            int iconIndex;
            extractIcon.GetIconLocation(GILInFlags.GIL_FORSHELL, builder, 260, out iconIndex, out flags);
 
            //  Get the icon of the desired size.
            IntPtr largeIconHandle;
            IntPtr smallIconHandle;
            uint iconSize = 64;
            extractIcon.Extract(builder.ToString(), (uint) iconIndex, out largeIconHandle, out smallIconHandle, iconSize);
 
            //  Create the .NET icon object.
            var icon = Icon.FromHandle(largeIconHandle);

GeneralRe: Windows iconsmemberMayzz10-Feb-13 2:17 
This is really great. Your source is really complete.
Thank you for your advice and congratulations for this wonderful library.
GeneralRe: Windows iconsmvpDave Kerr10-Feb-13 3:05 
My pleasure Smile | :)

QuestionAnd again...memberBrisingr Aerowing16-Jan-13 15:06 
A 5 from me!

Bob Dole
The internet is a great way to get on the net.

D'Oh! | :doh: 2.0.82.7292 SP6a

AnswerRe: And again...mvpDave Kerr16-Jan-13 20:31 
Thanks!

GeneralMy vote of 5memberSkyRunner11-Jan-13 10:08 
Always good ...
Dave, in case you haven't got my email I'll send you another mail during the next day, I provided more contact options in the mail. Sorry for the previous delay.
waiting for the co-working project.
GeneralRe: My vote of 5mvpDave Kerr11-Jan-13 10:38 
Hi, thanks for the vote - I've got your email just haven't got round to replying yet, have been very busy with work! Will send a response your way this weekend Smile | :)

GeneralRe: My vote of 5memberSkyRunner12-Jan-13 4:22 
Understand! Smile | :)
being in the same situation...
GeneralMy vote of 5memberEdo Tzumer9-Jan-13 22:47 
Keep putting those extensions and I'll keep sending those high five's Wink | ;)
 
Cheers for a nice work.
GeneralRe: My vote of 5mvpDave Kerr9-Jan-13 23:24 
Sweet! Thanks for the vote! The next extension type is the 'info tip handler' - provides customised tooltips for shell items - watch this space!

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 23 Feb 2013
Article Copyright 2013 by Dave Kerr
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid