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

.NET Shell Extensions - Shell Preview Handlers

, 20 May 2014 MIT
Rate this:
Please Sign up or sign in to vote.
Quickly create Shell Preview Handlers for Windows or Outlook using .NET!

Introduction

Shell PreviewHandlers are dlls that can be registered in the system to allow you to create visually rich previews for items that are displayed directly in Windows Explorer. They can also be used in Outlook. In this article I will show you how to create a Preview Handler extension using .NET and a library called SharpShell. We'll create a Preview Handler that shows the user all of the images contained in an icon file when one is selected, just as below:

Above: A folder full of icons on my machine. When an icon is selected, and the preview pane is open, the Icon Preview Handler extension shows each of the different icon images in the file.

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

How Preview Handlers Work

Preview handlers are pretty funky. Ever since adding support for them to my SharpShell library, I've kept the preview pane open and been surprised by how useful they can be. Essentially, what the system does when you select a file in Explorer and have the preview handler open is:

  1. Check the registry to see if there's a preview handler for the file type.
  2. If there is, create an instance of the prevhost process and load the preview handler dll into it.
  3. Instantiate the object that implements IPreviewHandler.
  4. Tell that object what the parent window is, what its size is etc.
  5. Tell the object to render a preview, supplied file details (either the path, stream of the file or IShellItem).
This is a pretty easy process to hook into, Preview Handlers are well documented. However, writing all of the COM plumbing requires a lot of work with pinvoke, the smallest mistake will cause exceptions to be thrown in explorer and generally be a hassle. So with SharpShell, the plumbing is done for you, leaving you with the task of actually creating the UI and logic you want.

Let's get started creating the Icon Preview Handler.

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 'IconPreviewHandler'. Rename the 'Class1.cs' file to 'IconPreviewHandler.cs'.

Now add the following references:

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

These references contain various useful bits and pieces that other parts of the SharpShell library will need, such as icons and context menus.

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 Core Library' zip file at the top of the article and add a reference to the 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.





Step 3: Deriving from SharpPreviewHandler

The now that we've set up the project, we can derive the IconPreviewHandler class from SharpPreviewHandler. SharpPreviewHandler is the base class for Preview Handler Shell Extensions.

public class IconPreviewHandler : SharpPreviewHandler
{
} 

For a preview handler, there is one abstract function in the base class that we must implement in the derived class.

DoPreview

protected abstract PreviewHandlerControl DoPreview();

DoPreview is called when the class must create the user interface for the preview. It returns a PreviewHandlerControl which is just a UserControl with a few extra virtual functions. Typically, the following will happen:

  1. The derived class will create an instance of some user control.
  2. The user control will be populated with data based on what's in the SelectedFilePath property.
  3. The user control will be returned.

That's actually all you need to do!

So for this project, add a new User Control and call it IconHandlerPreviewControl. Change its base class to 'PreviewHandlerControl' and add the 'DoPreview' function to your IconPreviewHandler class:

/// <summary>
/// DoPreview must create the preview handler user interface and initialize it with data
/// provided by the shell.
/// </summary>
/// <returns>
/// The preview handler user interface.
/// </returns>
protected override PreviewHandlerControl DoPreview()
{
    //  Create the handler control.
    var handler = new IconPreviewHandlerControl();
 
    //  Do we have a file path? If so, we can do a preview.
    if(!string.IsNullOrEmpty(SelectedFilePath))
        handler.DoPreview(SelectedFilePath);
 
    //  Return the handler control.
    return handler;
}

Here we do just as I've described. We actually call the 'DoPreview' function of the IconHandlerPreviewControl, which has not yet been written. If you need the details you can get the source code from the top of the article, but actually the code is straightforward enough to drop in here:

public void DoPreview(string selectedFilePath)
{
    //  Load the icons.
    try
    {
        var multiIcon = new MultiIcon();
        multiIcon.Load(selectedFilePath);
 
        //  Add the icon images.
        foreach (var iconImage in multiIcon.SelectMany(singleIcon => singleIcon))
            iconImages.Add(iconImage);
 
        //  Add the icons to the control.
        AddIconsToControl();
    }
    catch
    {
        //  Maybe we could show something to the user in the preview
        //  window, but for now we'll just ignore any exceptions.
    }
}

Here we use the excellent IconLib library to load the icons, then store each image. We then call AddIconsToControl to actually add them to the UI:

private void AddIconsToControl()
{
    //  Go through each icon, keep track of y pos.
    int yPos = 12;
    foreach (var iconImage in iconImages)
    {
        //  Create the description.
        var descriptionLabel = new Label
        {
            Location = new Point(12, yPos),
            Text = string.Format("{0}x{1} - {2}",
                                 iconImage.Size.Width, iconImage.Size.Height, iconImage.PixelFormat),
            AutoSize = true,
            BackColor = Color.White
        };
        yPos += 20;
 
        //  Create the picture box.
        var pictureBox = new PictureBox
        {
            Location = new Point(12, yPos),
            Image = iconImage.Icon.ToBitmap(),
            Width = iconImage.Size.Width,
            Height = iconImage.Size.Height
        };
        yPos += iconImage.Size.Height + 20;
        panelImages.Controls.Add(descriptionLabel);
        panelImages.Controls.Add(pictureBox);
 
        //  Keep track of generated labels.
        generatedLabels.Add(descriptionLabel);
    }
}

There are some member variables that I'll not describe here, but this is the core of the functionality - go through each icon image and create a label that describes it and a picture box with the image in 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. This because our class is a COM server and must be visible to other code trying to use it.

[ComVisible(true)]
public class IconPreviewHandler : SharpPreviewHandler 

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:

Finally, we need to associate our extension with some the types of shell items we want to use it for. We can do that with the COMServerAssociation attribute:

[COMServerAssociation(AssociationType.ClassOfExtension, ".ico")]
[DisplayName("Icon Preview Handler")]
public class IconPreviewHandler : SharpPreviewHandler 

So what have we done here? We've told SharpShell that when registering the server, we want it to be associated with icofile classes in the system.

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.

We've also set the DisplayName attribute. The DisplayName is used in the registry and also if the handler fails (when a message saying 'The Icon Preview Handler Failed To Load' or similar will be shown in the preview pane).

We're done! Building the project creates the IconPreviewHandler assembly, which can be registered as a COM server to add the extension to the system, allowing you to quickly see what images an ico file contains.

Advanced Features

The PreviewHandlerControl class has the following virtual functions:

/// <summary>
/// Sets the color of the background, if possible, to coordinate with the windows
/// color scheme.
/// </summary>
 /// <param name="color">The color.</param>
protected virtual void SetVisualsBackgroundColor(Color color){}
 
/// <summary>
/// Sets the color of the text, if possible, to coordinate with the windows
/// color scheme.
/// </summary>
/// <param name="color">The color.</param>
protected virtual void SetVisualsTextColor(Color color){}
 
/// <summary>
/// Sets the font, if possible, to coordinate with the windows
/// color scheme.
/// </summary>
/// <param name="font">The font.</param>
protected virtual void SetVisualsFont(Font font){}

You can override these to change the colours and fonts of your UI to match those defined in the current system theme.

Debugging the Shell Extension

If you have seen any of my other articles on .NET Shell Extensions, 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 IconPreviewHandler.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 choose 'Test Server in Test Shell'. This opens up the Test Shell window. Any files that are associated with the server are highlighted in bold. You can click on one of these files and choose 'Preview Handler' on the right -this will show the preview handler, and also let you debug into it.

Tip: You don't have to install or register the server to test it in the Test Shell.

If you want to debug in a more realistic scenario, you can install and register the server (using the 'Server' menu) and open the test shell, then choose 'Open Shell Dialog'. This will create a File Open dialog, which you can use to invoke the preview handler functionality. Remember, if you want to debug into the preview handler, you must attach to the process 'prevhost.exe', not 'explorer.exe'.

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

Building Preview Handlers: A useful source of documentation on MSDN about Preview Handlers.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

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

Comments and Discussions

 
QuestionUnable to get IconPreviewHandler Working in Windows Explorer PinmemberMember 379724128-Oct-14 13:51 
AnswerRe: Unable to get IconPreviewHandler Working in Windows Explorer PinmvpDave Kerr28-Oct-14 23:13 
GeneralRe: Unable to get IconPreviewHandler Working in Windows Explorer PinmemberMember 379724129-Oct-14 14:00 
GeneralRe: Unable to get IconPreviewHandler Working in Windows Explorer PinmvpDave Kerr30-Oct-14 0:35 
QuestionFlickering PinmemberShawn Rubie1-Oct-14 10:21 
AnswerRe: Flickering PinmvpDave Kerr13-Oct-14 21:50 
GeneralSome issues, but got it working PinmemberShawn Rubie29-Sep-14 11:13 
GeneralRe: Some issues, but got it working PinmvpDave Kerr13-Oct-14 21:54 
BugError in IPreviewHandler.SetRrect PinmemberMember 907599029-Sep-14 2:53 
GeneralRe: Error in IPreviewHandler.SetRrect PinmvpDave Kerr30-Sep-14 18:50 
GeneralMy vote of 5 PinprofessionalVolynsky Alex20-May-14 14:21 
GeneralRe: My vote of 5 PinmvpDave Kerr26-May-14 22:56 
QuestionRe: My vote of 5 PinprofessionalVolynsky Alex27-May-14 0:26 
QuestionLicence other than CPOL? PinmemberSin Jeong-hun20-May-14 0:20 
AnswerRe: Licence other than CPOL? PinprofessionalBrisingr Aerowing20-May-14 0:37 
GeneralRe: Licence other than CPOL? PinmvpDave Kerr20-May-14 9:32 
GeneralRe: Licence other than CPOL? PinmemberSin Jeong-hun27-Aug-14 16:34 
GeneralRe: Licence other than CPOL? PinmvpDave Kerr7-Sep-14 8:30 
Questioncode that works in the ServerManager but not in the actual Windows Explorer PinmemberMember 449650122-Apr-14 4:35 
AnswerRe: code that works in the ServerManager but not in the actual Windows Explorer PinmvpDave Kerr26-Apr-14 22:56 
GeneralRe: code that works in the ServerManager but not in the actual Windows Explorer PinmemberMember 1080062012-May-14 0:58 
QuestionNo logs are present PinmemberMember 449650121-Apr-14 22:56 
AnswerRe: No logs are present PinmvpDave Kerr22-Apr-14 2:03 
QuestionPreview handler working in the ServerManager but not in Windows Explorer PinmemberMember 449650117-Apr-14 0:43 
AnswerRe: Preview handler working in the ServerManager but not in Windows Explorer PinmvpDave Kerr21-Apr-14 11:30 
BugRe: Preview handler working in the ServerManager but not in Windows Explorer PinmemberMember 978493220-Oct-14 4:33 
QuestionAnother question not exactly related to the article.... PinmemberMember 51837431-Mar-14 0:05 
AnswerRe: Another question not exactly related to the article.... PinmvpDave Kerr4-Apr-14 7:14 
QuestionQuestion not exactly related to the article... PinprofessionalBrisingr Aerowing21-Mar-14 15:57 
AnswerRe: Question not exactly related to the article... PinmvpDave Kerr22-Mar-14 13:43 
GeneralRe: Question not exactly related to the article... PinprofessionalBrisingr Aerowing22-Mar-14 14:06 
GeneralMy vote of 5 PinprofessionalPrasad Khandekar14-Sep-13 7:33 
GeneralRe: My vote of 5 PinmvpDave Kerr14-Sep-13 22:15 
QuestionIs there a way to get the size of the preview window? PinmemberBig9erFan13-Mar-13 15:22 
AnswerRe: Is there a way to get the size of the preview window? PinmvpDave Kerr14-Mar-13 7:14 
GeneralMy vote of 5 PinmemberEdo Tzumer27-Jan-13 2:40 
GeneralRe: My vote of 5 PinmvpDave Kerr14-Mar-13 3:28 
Question5! PinmemberBrisingr Aerowing26-Jan-13 6:00 
AnswerRe: 5! PinmvpDave Kerr26-Jan-13 6:09 
And again, thanks Smile | :)

GeneralRe: 5! PinmemberBrisingr Aerowing26-Jan-13 6:49 
GeneralRe: 5! PinmvpDave Kerr26-Jan-13 7:46 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411023.1 | Last Updated 20 May 2014
Article Copyright 2013 by Dave Kerr
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid