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

.NET Shell Extensions - Shell Property Sheets

5.00/5 (18 votes)
7 Apr 2013CPOL7 min read 100.4K   3K  
Use .NET to rapidly build Shell Property Sheets

Introduction

In this article, I will show you how to use SharpShell to quickly create Shell Property Sheet extensions. These are extensions that add extra pages to the property sheets shown for shell items such as files, network shares, folders and so on. Here's a screenshot of what we'll create in the tutorial.

Image 1

Above: A screenshot of a Shell Property Sheet Extension. This sample adds a 'File Times' page that lets the file times for a file be modified.

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

What is a Shell Property Sheet Extension

Technically, a Shell Property Sheet extensions is a COM server that exports an object that implements the interface IShellPropSheetExt. This extension lets us do two things - add property pages to a shell property sheet, or replace them.

Now, typically working with old-fashioned APIs like this can be quite difficult when you're in the managed world. You need to do a lot of interop, you need to import functions from the Win32 APIs to create property pages, create message pumps and so on. We're going to use the SharpShell library to do all of the plumbing for us, leaving us with the much easier task of simply creating property pages and adding them to our extension class.

Now that we know what a Property Sheet Extension is, we can get started implementing one.

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

Now add the following references:

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

Rename 'Class1.cs' to 'FileTimesPropertySheet.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:

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. This is the best way - you'll always get the latest version of the library and it'll add any dependencies automatically.

Image 2

Add Reference

Download the 'SharpShell Core 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 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 Code Project articles will have the version that was available at the time of writing.

Step 3: Derive from SharpPropertySheet

We must have a class that derives from SharpPropertySheet - this will be the main extension class. Let's take the file we named 'FileTimesPropertySheet' and derive from SharpPropertySheet now:

C#
public class FileTimesPropertySheet : SharpPropertySheet

Now we're going to have to create implementations of two abstract functions:

CanShowSheet

This function returns a bool. If the result is true, then the property sheet pages we create are going to be shown, otherwise we won't show them. Here's our implementation:

C#
protected override bool CanShowSheet()
{
    //  We will only show the resources pages if we have ONE file selected
    return SelectedItemPaths.Count() == 1;
} 

Because we're derived from SharpPropertySheet, we have a property called 'SelectedItemPaths' - this is an enumerable set of strings that store the paths selected by the user when the sheet was invoked. In our example, we're only going to show the pages if we have just one file selected.

CreatePages

This function returns a set of SharpPropertyPage objects - these are the actual property pages we're going to add to the shell property sheet.

Here's how our implementations will look:

C#
protected override IEnumerable<SharpPropertyPage> CreatePages()
{
    //  Create the property sheet page
    var page = new FileTimesPropertyPage();
 
    //  Return the pages we've created
    return new[] {page};
}

This isn't going to work until we create the 'FileTimesPropertyPage' class, we'll do that soon!

Step 4: Create the Associations

We need to define what shell objects this extension is going to be used for. This is done by decorating the extensions with the COMServerAssociation attribute. Here's how we can apply this extension will all files.

C#
[COMServerAssociation(AssociationType.AllFiles)]
public class FileTimesPropertySheet : SharpPropertySheet 

There are a number of different types of associations we can make - we can associate with files, folders, files with specific extensions and so on. There's a more complete guide here on the page COM Server Associations.

Step 5: Create the Property Sheet Page

You can follow this step as many times as you need - one property sheet extension can add any number of pages.

First, add a UserControl to your class library, call it 'FileTimesPropertyPage'. Now open up the code-behind for the control and change the parent class from 'UserControl' to 'SharpPropertyPage'.

Here's the first thing we can do - set the title of the property page:

C#
/// <summary>
/// The FileTimesPropertyPage class
/// </summary>
public partial class FileTimesPropertyPage : SharpPropertyPage
{
    /// <summary>
    /// Initializes a new instance of the <see cref="FileTimesPropertyPage"/> class
    /// </summary>
    public FileTimesPropertyPage()
    {
        InitializeComponent();
 
        //  Set the page title
        PageTitle = "File Times";
 
        //  Note: You can also set the icon to be used:
        //  PageIcon = Properties.Resources.SomeIcon;
    }

We set the title of the page by setting the PageTitle property. We can also optionally set the icon for the page by setting the PageIcon property.

Now there are a bunch of virtual functions in the SharpPropertyPage that we can override if we choose to. Here, I'll go through the key ones.

OnPropertyPageInitialised

This function is called when the page has been created and is about to be displayed to the user. If a property sheet is shown, but the user doesn't click on this particular page, then this function won't be called. Any initialization should be done here. In this function, you are provided with the parent SharpPropertySheet object, which contains the selected file paths.

C#
private string filePath;
 
/// <summary>
/// Called when the page is initialized
/// </summary>
/// <param name="parent">The parent property sheet.</param>
protected override void OnPropertyPageInitialised(SharpPropertySheet parent)
{
    //  Store the file path
    filePath = parent.SelectedItemPaths.First();
 
    //  Load the file times into the dialog
    LoadFileTimes();
}

In our example, we store the selected file path and then update the UI from the file times (through the LoadFileTimes function).

What other functions should we implement? Well, almost always, we'll want to handle 'OK' and 'Apply':

OnPropertySheetOK and OnPropertySheetApply

C#
/// <summary>
/// Called when apply is pressed on the property sheet, or the property
/// sheet is dismissed with the OK button
/// </summary>
protected override void OnPropertySheetApply()
{
    //  Save the changes
    SaveFileTimes();
}
 
/// <summary>
/// Called when OK is pressed on the property sheet
/// </summary>
protected override void OnPropertySheetOK()
{
    //  Save the changes
    SaveFileTimes();
}

Nothing too advanced here - in this class, we're just going to save any changes the user has made.

Property Sheet Pages Quick Reference

Here's a quick reference for property sheet pages.

Function Usage
OnPropertyPageInitialised Called when the page must be initialized.
OnPropertyPageSetActive Called when the page is about to be presented to the user. This is always called after OnPropertyPageInitialised.
OnPropertyPageKillActive Called when the page is about to be deselected.
OnPropertySheetApply Called when the Apply button is pressed.
OnPropertySheetOK Called when the OK button is pressed.
OnPropertySheetCancel Called when the Cancel button is pressed.
OnPropertySheetClose Called when the cross at the top right of the dialog is pressed.
SetPageDataChanged Call this function to enable the 'Apply' button.

Step 6: Expose to COM

We're going to need to do a few things now to allow our class to be created by the shell.

First, we must add the COMVisible attribute to our class:

C#
[ComVisible(true)]
[COMServerAssociation(AssociationType.AllFiles)]
public class FileTimesPropertySheet : SharpPropertySheet 

Now we must sign the assembly. 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 can be debugged in the following way:

  1. Run the 'Server Manager' project from the source code.
  2. Open the server you have built, select it, choose 'Install' and then 'Register' for your processor architecture.
  3. Now press the 'Open Shell Dialog' button.
  4. This will show Shell Open Dialog. If you right click on a file and choose the properties, then the extension will be shown and any breakpoints that you have enabled will work successfully, as the shell window is in the address space that we've got a debugger attached too.
  5. Once you're done debugging, you can close the shell open dialog and unregister/uninstall it.

Installing and Registering the Shell Extension

See the article Shell Context Menus for a guide on how to register SharpShell extensions.

History

  • 8th April, 2013: Initial version

License

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