Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

.NET Shell Extensions - Shell Icon Overlay Handlers

5.00/5 (32 votes)
14 Sep 2013CPOL7 min read 199.7K   6.7K  
Create Shell Icon Overlay Handlers using .NET!

Introduction

Shell Icon Overlay Handlers can be really useful. They let you display an icon overlay over shell objects to provide extra information. Programs like Dropbox use these overlays to show whether icons are synchronized or not. In this article, I'll show you how to use SharpShell to create Shell Icon Overlay Handlers in no time at all!

Below is a screenshot of the sample we'll create - it shows a padlock over files that are read-only.

Image 1

Above: Some text files in a folder. With this shell icon extension installed, read-only files have a padlock overlay.

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
  8. .NET Shell Extensions - Shell Property Sheets

Getting Started

Shell Extensions are normally written in C or C++. Until the .NET Framework 4 was released, it was not possible to reliably create a Shell Extension using .NET. Now you can. With the SharpShell library, all of the COM plumbing is handled for you, and you simply have to create a class for the extension, decorate it with some attributes and override some functions.

To show how easy this is, let's see the full code for the read-only file icon overlay handlers.

Our Goal

Here's the full code for the read-only overlay handler. It's very lean indeed - now that we've seen it in full, I'll describe how to create your own extension step-by-step.

C#
/// <summary>
/// The ReadOnlyFileIconOverlayHandler is an IconOverlayHandler that shows
/// a padlock icon over files that are read only.
/// </summary>
[ComVisible(true)]
public class ReadOnlyFileIconOverlayHandler : SharpIconOverlayHandler
{
    /// <summary>
    /// Called by the system to get the priority, which is used to determine
    /// which icon overlay to use if there are multiple handlers. The priority
    /// must be between 0 and 100, where 0 is the highest priority.
    /// </summary>
    /// <returns>
    /// A value between 0 and 100, where 0 is the highest priority.
    /// </returns>
    protected override int GetPriority()
    {
        //  The read only icon overlay is very low priority
        return 90;
    }
 
    /// <summary>
    /// Determines whether an overlay should be shown for the shell item 
    /// with the path 'path' and
    /// the shell attributes 'attributes'
    /// </summary>
    /// <param name="path">The path for the shell item. This is not necessarily the path
    /// to a physical file or folder.</param>
    /// <param name="attributes">The attributes of the shell item.</param>
    /// <returns>
    ///   <c>true</c> if this an overlay should be shown for the specified item; 
    ///  otherwise, <c>false</c>.
    /// </returns>
    protected override bool CanShowOverlay(string path, FILE_ATTRIBUTE attributes)
    {
        try
        {
            //  Get the file attributes
            var fileAttributes = new FileInfo(path);
 
            //  Return true if the file is read only, meaning we'll show the overlay
            return fileAttributes.IsReadOnly;
        }
        catch (Exception)
        {
            return false;
        }
    }
 
    /// <summary>
    /// Called to get the icon to show as the overlay icon
    /// </summary>
    /// <returns>
    /// The overlay icon
    /// </returns>
    protected override System.Drawing.Icon GetOverlayIcon()
    {
        //  Return the read only icon
        return Properties.Resources.ReadOnly;
    }
} 

The bulk of this is comments - so you can see, creating overlay handlers with SharpShell is pretty easy!

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 'ReadOnlyFileIconOverlayHandler'.

Tip: If you add a reference to SharpShell via Nuget, the references below will be added automatically.

Now add the following references:

  1. System.Windows.Forms
  2. System.Drawing

Rename the 'Class1.cs' file to 'ReadOnlyFileIconOverlayHandler.cs'.

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.

Image 2

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 SharpIconOverlayHandler

Now things get interesting. Derive your ReadOnlyFileIconOverlayHandler class from SharpIconOverlayHandler:

C#
/// <summary>
/// The ReadOnlyFileIconOverlayHandler is an IconOverlayHandler that shows
/// a padlock icon over files that are read only.
/// </summary>
public class ReadOnlyFileIconOverlayHandler : SharpIconOverlayHandler

Now we must implement the abstract members of the class. Right click on the SharpIconOverlayHandler part of the line and choose 'Implement Abstract Class'. This will create empty implementations of the functions we need to provide.

C#
 /// <summary>
/// The ReadOnlyFileIconOverlayHandler is an IconOverlayHandler that shows
/// a padlock icon over files that are read only.
/// </summary>
public class ReadOnlyFileIconOverlayHandler : SharpIconOverlayHandler
{
    protected override int GetPriority()
    {
        throw new NotImplementedException();
    }
 
    protected override bool CanShowOverlay(string path, FILE_ATTRIBUTE attributes)
    {
        throw new NotImplementedException();
    }
 
    protected override System.Drawing.Icon GetOverlayIcon()
    {
        throw new NotImplementedException();
    }
}

Here's a description of the functions and how they should be used:

GetPriority

This function must return the priority of the handler. The priority is used when there are two icon overlays for a single file - the shell will use the icon with the highest priority. Just to be confusing, the highest priority is 0 - the lowest priority is 100.

CanShowOverlay

Every time a shell item will be displayed, this function will be called. The path to the shell item is provided in the path parameter. The FILE_ATTRIBUTE flags are also provided (these are attributes like FILE_ATTRIBUTE_DIRECTORY). Return true if you want the icon overlay to be shown for the specified file.

GetOverlayIcon

This function must return a standard .NET System.Drawing.Icon object that is the overlay icon to use.

Step 3: Implement the Functions

Our implementation of these functions is going to be as below. First, GetPriority:

C#
protected override int GetPriority()
{
    //  The read only icon overlay is very low priority.
    return 90;
}

This overlay is useful, but not as important as say, TortoiseSVN's state icons, so let's make the priority low.

Now, CanShowOverlay:

C#
protected override bool CanShowOverlay(string path, FILE_ATTRIBUTE attributes)
{
    try
    {
        //  Get the file attributes
        var fileAttributes = new FileInfo(path);
 
        //  Return true if the file is read only, meaning we'll show the overlay
        return fileAttributes.IsReadOnly;
    }
    catch (Exception)
    {
        return false;
    }
} 

We're going to return true (to show the overlay) only if we can get the file attributes without throwing an exception, and if those attributes indicate readonly. (Why don't I use FILE_ATTRIBUTE_READONLY? Well, apparently it doesn't work properly for directories).

The last function, GetOverlayIcon:

C#
protected override System.Drawing.Icon GetOverlayIcon()
{
    //  Return the read only icon
    return Properties.Resources.ReadOnly;
}

In the project, I added an icon named 'ReadOnly' to the resources - this just returns it.

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.

C#
[ComVisible(true)]
public class ReadOnlyFileIconOverlayHandler : SharpIconOverlayHandler

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:

Image 3

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 up the Server Manager, and load the extension. Choose 'Install' and then 'Register', now open the Test Shell and choose 'Shell Open Dialog' - now breakpoints in your extension should be hit as you move around the file system.

Image 4

Above: The Server Manager

Image 5

Above: The Test Shell

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.

Cheat Sheet

Here are some useful tips for working with SharpShell and Icon Overlay Handlers.

  • Does it seem like your overlay isn't working? Open regedit and check the path:
    HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayHandlers
    Windows supports only a limited number of handlers - in alphabetical order. There may be too many installed.
  • If you need to log events, Run the Server Manager as an Administrator and choose 'Diagnostics > Enable SharpShell Log'. You can now use the functions:
    Log
    LogError
    LogWarning
    in your server class, and these messages will be written to the Windows Event Log (under 'Application').

History

  • 14th September, 2013: Initial version

License

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