|
|
|
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