In Depth Banner
Skip Navigation Links

Select your preferred language

Managed Shell Extensions.

This is the first in a series of occasional articles that will create some handy shell-extension objects in managed code. Look out for more in future editions.

The Thumbnail Preview extension

The Windows operating system is built on COM, at least, the shell is.  Since the days of windows 95 it has been possible to extend the abilities of the operating system using COM components.  For example, you can extend the shell and add context menu entries and handlers, property sheet extensions,  column handlers that enable you to add information to the explorer detail comments and many others. 

Just a few years ago creating a shell extension was a daunting task simply because the bulk of programming COM required to create even the simplest object was annoyingly large and unnecessarily complex.  The .NET framework however, has greatly simplified the task of creating com objects and so .net is a natural and simple solution for extending the shell. 

The techniques for creating shell extensions are all relatively similar and so learning to create one type of shell extension with .net will provide enough inspiration to go ahead and create others although you will definitely see more of them in future Well Formed editions.  This article deals with one of the more visual aspects of shell extensions,  creating a thumbnail preview for a specific file type. This is particularly useful to those of you who have created applications using Windows Forms that have graphical content that will benefit from the ability to see the file, or at least a thumbnail representation of the file, in explorer.

File types and thumbnail views. 

For every file type that the shell knows about there is an entry in the registry that enables the shell to associate the file type with one or more programs or actions.  One of these actions is to generate a thumbnail view of the file which can be displayed in explorer.  Some files, such as Microsoft office documents, will create a small bitmap view of the file which is stored along with the rest of the file data to be extracted at the correct time.  Other documents have the ability to create a thumbnail view on the fly from the information stored in the file.  In either case the process that retrieves this thumbnail view is identical and depends upon a couple of interfaces defined by the shell.  The first of these interfaces is IExtractImage the other is IPersistFile. in order to create a shell extension in .net we have to create a managed class that implements these interfaces and associate this class with the file type saved by our hypothetical application.

There are numerous ways of getting the definition for interfaces such as these, some more complex than others, but the one that I prefer, while seeming at the outset to be complex,  provides benefits that make the little bit of extra work well worth its while.  First is necessary to discover the type library in which the interface is defined.  Once this is known, it's possible to use MIDL to create a second type library which contains the interfaces and structures required by the managed class that will become the shell extension.  Then, this type library is used to create an Interop assembly that can be referenced by our code and which provides all the information necessary to interface with the shell.  The following simple steps show this process.

Step 1. Create a guid. This will be used as the COM interface guid that associates the file type with the shell extension. This will be used later in the code too.

Step 2. Create an IDL file that imports the correct libraries and declares the needed interfaces. The GUID isn't important in this instance although there's no harm in adding it.


[
	uuid(E134E0A2-5682-41d4-AF22-6F516844C094)
]
library ExtractImageInterop
{
	import "SHOBJIDL.IDL";

	interface IExtractImage;
	interface IPersistFile;
};

Step 3. Compile the idl into a type library using midl. Unfortunately MIDL doesn't take any notice of the current include system variable and invariably has to include header files and header files from the system.  Because I do this operation quite a lot, I created a "MakeMIDL" directory that contains all of the IDL and system header files plus MIDL.exe which is used as a temporary storage space for creating type libraries in this manner.

The resulting type library (TLB) file may be referred to in any visual studio project and a primary Interop DLL will be compiled for you automatically. Earlier I mentioned advantages that were to be gained from this seemingly complex method And the great thing about creating your interop assemblies in this fashion is that all of the structures and definitions required by all of the interfaces are also compiled along with the interfaces themselves.  This means for example, that if you're interface requires a RECT structure in one of its parameters then the Interop assembly will contain a definition for the structure and you won't have to put in extra work recreating it yourself. 

Creating the shell extension DLL.

Using the standard new project wizard, create a simple class library in visual studio.  Rename the generated Class1 class to something more appropriate and ensure your newly created type library is added to the references.  At the top of the file declare that you are using or importing the Interop assembly and then go on to add the two interfaces IExtractImage and IPersistFile in the class declaration.  If you're lucky enough to be using a copy of visual studio 2003 and programming in C# you will be able to press the tab key as you type the interface names and a wizard will create the implementations for you.  If you're using an earlier version of visual studio or programming in visual basic you can add the implementations through the class browser as normal.  You also need to add the attribute that associates the GUID, which you created earlier, with the managed class.   This listing shows the different stages of the shell extension.  Refer to stage one for the code so far. 

Before we go ahead and flesh out the implementations of these interfaces let's just take a look at what the shell extension is required to do by the operating system. 

The registry entries.

The registry hive HKEY_CLASSES_ROOT contains an entry for each of the file types the shell knows about. Let's say our file has the extension ".WFF" for Well Formed File. This entry must have a ShellEx key which in turn contains a key called "{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}" This is in fact the GUID of IExtractImage. The default value of that key must be the GUID of our shell extension. Later in the registry in HKEY_CLASSES_ROOT\CLSID there will be an entry for the shell extension that tells the shell which DLL to load. Figure 1 shows the relationship between the file extension and the class ID

Figure 1. Relationship between file, shell extension and class ID.

Note that you don't need to be concerned about the actual entries in the CLSID portion of the registry as this is done for you by regserver32 when the shell DLL is registered. 

The sequence of events. 

With thumbnail view selected and the file visible in explorer, the system will examine the registry entries to find out which shell extensions are provided.  In this case, our extension DLL is loaded and queried first of all for the IPersistFile Interface.  IPersistFile.Load is used to specify which file is required, then the managed class is queried again for the IExtractImage interface. The IExtractImage.GetLocation method is used to specify the size of thumbnail required by the system and to ascertain the capabilities of the shell extension, then IExtractImage.Extract is called to get the image. Note that this method requires a pointer so we can use the Marshal class to provide a pointer to the bitmap handle that's created.

Something to preview. 

In order to have something to preview,  there has to be a file with some kind of graphical format.  For the purposes of this demonstration an application has been created that enables you to understand how an image can be generated on the fly for the thumbnail  preview.   The application consists of two parts.  The first part being a DLL that's responsible for maintaining a file format and rendering the graphics.  The second part is a simple windows forms application which hosts the class contained in the first DLL. the application is structured in this way so that the shell extension can also use the DLL to load the file and create a graphic of its contents.  This listing contains the code for the DLL and this listing contains the code for the host application. Note how the graphic DLL is given a strong name. This is so that it can be installed in the Global Assembly Cache (GAC) and accessed system-wide. The shell extension DLL will require access to the DLL and will also be strongly named and may be installed in the GAC.

The graphics generated are trivial but ideal for the purposes of this demonstration. A file that consists of a shape and a colour is saved on disk. The file generates a cross, circle or square in red, yellow or green. When the file is previewed, you'll see the corresponding graphic. Figure 2 shows the graphics created by the application.

Implementation.

Loading the file

The IPersistFile.Load method is called to specify which file a thumbnail should be generated for. The filename is saved

Specifying the size of the thumbnail

The shell provides you with a size for the thumbnail. It also enables you to specify other information such as the way your extension will provide the image, for example immediately or in an asynchronous manner. The IExtractImage.GetLocation method is called and the desired size is stored. In this implementation the priority of the extension is set to normal. For more information on the IExtractImage interface, refer to the MSDN help

Creating and returning the thumbnail

Finally the IExtractImage.Extract method is called and the image can be created. In this implementation the image is created on the fly by using the original DLL to load and draw an object onto an in-memory bitmap sized to comply with the specifications sent in the GetLocation method. The bitmap is used to generate a Win32 bitmap handle (HBITMAP) which is returned to the shell. Note in particular how the Marshal class is used to pass the pointer to the HBITMAP back to the shell.

Now, when a .wff file is seen in any Explorer window that is configured to view thumbnails, the shape and colour is seen and the file can be easily selected. as seen in figure 2.

Figure 2. Thumbnails by managed code.

Summary.

In this article you've seen how to create type libraries for COM interop with the system that has added benefits, understood how the shell associates a thumbnail generator with it's file type and seen how to implement COM objects in managed code that play well with the operating system. The principles shown will enable you to create custom thumbnail generators for graphical file types you create yourself and enhance you're users experience of your applications. Keep an eye out in future editions for more managed shell extensions.

You can find the Source Code files here.

Return to the main index

 

Copyright © Bob Powell 2003-2009. All rights reserved