Hosting Preview Handlers in Windows Forms Applications

Preview Handlers are a concept introduced in Windows Vista (and supported in later operating systems). Most prominently used by Windows Explorer (and notably in Outlook 2007), they allow third-party developers to provide lightweight, interactive previews of data that can be hosted within other applications. You might use them to preview documents in a custom file explorer, or from somewhere that Windows Explorer cannot go, such as files stored as BLOBs in a database. Any exercise that involves reviewing or managing documents benefits extremely from a mechanism that will preview the content without opening another program, and this is it!

From an implementation perspective, preview handlers are basically borderless windows that are bound to a window or control in your application. Their operation and content can be controlled, but their UI is isolated. All preview handlers implement the COM interface IPreviewHandler and are associated with file formats within the Windows Registry. At the time of writing, there is no official managed API for hosting preview handlers in Windows Forms applications, so I have written my own.

Windows Forms app with an embedded Microsoft Word previewer

In my implementation, PreviewHandlerHost is a custom control to which the preview handler is bound. With each filename/stream that is opened, the appropriate handler is loaded and displayed. To display a preview handler in a Windows Forms app, given the filename of the content you want to preview, the following must be performed:

Locate the registry key in HKEY_CLASSES_ROOT that corresponds to the file extension. Within the ShellEx key, there should be a key with the GUID {8895b1c6-b41f-4c1c-a562-0d564250836f}. Within this key, there should be a GUID that identifies the COM type that implements IPreviewHandler. If you cannot locate the key here, try the file type’s class key – this corresponds to the default value of the first key mentioned above.

The relevant COM interfaces have to be manually declared and mapped using the [ComImport] attribute (so it is necessary to know their GUIDs and structure).

IInitializeWithStream cannot be used directly with System.IO.Stream, so it was necessary to write a wrapper class that implements System.Runtime.InteropServices.IStream.

The usual COM clean-up routine is required (Marshal.FinalReleaseComObject()) in order to release resources – this is integrated into the control’s Dispose() method.

The finished control can simply be dropped onto a Form, and will display the preview after a call to the Open() method. If you resize the control while a preview handler is active, it will be resized accordingly. If no file has been loaded or an error is encountered, it will be displayed on the empty control. You can also explicitly unload a file by calling UnloadPreviewHandler(). Preview handlers may have transparent backgrounds (for example, the Microsoft Office handlers use rounded borders), so the control permits this.

Final Words

Preview handlers are a powerful addition to file explorers, document management systems and the like, and they can be harnessed for use in .NET as well. Unfortunately, however, they are not the only mechanism that Windows Explorer and other previewers use to display content. Under Vista and Windows 7, for example, there are no preview handlers registered for images, plain text documents or HTML files. That means that PreviewHandlerHost is far from a holistic solution. Rather, it should be combined with existing constructs to preview a fuller range of file formats. However, in testing I was able to demonstrate support for most major office document formats and media files.

I hope you find it useful 🙂

Download

Note: You must add a reference to the System.Design assembly in order to compile the source code.
Also, there is a known issue where the Windows Media Player 11 preview handlers leave behind a black rectangle after unloading.

Unfortunately, I have no new information about the TXT Preview Handler; it just doesn’t seem to work in some environments. Thanks for the code snippet; it seems there are several different locations in the registry that preview handler GUIDs may be declared.

And in fact, I believe the best way to discover the preview handler association is to use AssocQueryString (http://msdn.microsoft.com/en-us/library/bb773471(v=VS.85).aspx) with ASSOCSTR_SHELLEXTENSION as the str parameter, and “{8895B1C6-B41F-4C1C-A562-0D564250836F}” (IPreviewHandler) for the pszExtra parameter. In return, pszOut will contain a GUID string. This API will take care of all the gory details 🙂

The complete source code is already provided. As stated in my post, the formats that can be previewed will depend on the handlers installed on your machine. If you can only preview Word files, then I presume you only have the appropriate preview handler for those.

Great post! It works very well when I’m trying to open a file directly on my disk using the filename but what I’m actually trying to do is to open a Stream object that comes from a Livelink database. Is it actually possible? When I download content from the Livelink database the Livelink webservice returns me a System.IO.Stream Object and when I try to use the Open(stream, GUID) method I’m not sure on how to create the GUID object parameter. If I create the GUID with the GetPreviewHandlerGUID with the extension of the file that I can get from the Livelink database and try to initialize it says “No such interface supported” since it believes it has been instantiate from a file type and not a stream (or it is what I understand from it). Of course I could get the Stream object, save it to a temporary file and preview it but I don’t think it’s a good practice. Any help would be greatly appreciated.

Antony, did you ever find a solution for previewing the stream? I’m having the same problem. I created another overload: Open(stream, fileExtension). I use the fileExtension to pull the GUID from GetPreviewHandlerGUID, but when the IPreviewHandler tries to run its DoPreview method, an error is returned from the COM object. Also, the stream is never “wrapped” because it appears to be a file (not a stream). Does anyone know what GUID needs to be passed to preview a stream?

Hi Bradley, This is excellent code – I have however found something very peculiar when previewing Word documents, an occurence of Winword starts up and if I then switch to a different type of file (e.g. pdf) Winword shuts down when calling the Marshal.FinalReleaseComObject(mCurrentPreviewHandler) inside the public bool Open(string filename) – this is great.

However if the last document being previewed is a Word document then even though the dispose method calls UnloadPreviewHandler and then Marshal.FinalReleaseComObject – Winword is left running – so you can end up with a whole load of Winword processes running.

Hi Andrew, I have to admit that this one has me stumped. The call to Unload() should be enough to remove any runtime dependency on Winword.exe, and even if the code in the Dispose() method was not sufficiently releasing the object, the shutting down of the runtime should cause the process to exit anyway. COM can be a mysterious beast at times, and I think this is just one of those instances. I have not made an attempt to replicate this, but if I find a solution, I will be sure to post about it.

void Preview_FormClosing(object sender, FormClosingEventArgs e)
{
//We seem to need to dispose of the previewhandler before the parent form is disposed
// otherwise Word does not get unloaded!!
PreviewHandlerHost1.Dispose();
}

No idea why this should matter – I guess it implies things are getting disposed of out of sequence.

Not sure if you are still reading this blog entry, but I ran across exactly the same issue that you describe above some years ago myself. At the time, I also found out that doing the unload and dispose at the parent level improved things, but even back then my testing showed that the problem would still happen every now and again. I was never happy with this outcome and in fact, the problem has been bugging me since that time.

This week I started reworking some code for other reasons and I decided to place the iPreviewHandler stuff into its own thread whilst I was at it. That is when the fun began and I finally realise what is going on.

Basically what is happening is that my application exits before Unload() has finished, which it can do because of the way iPreviewHandler works. I found this out purely by accident as I placed a block on my main thread whilst testing and I saw that Unload() never returned. No error message, it just never returned at all.

My first thoughts were that perhaps I was destroying the file stream too soon but if that is the problem its not the one that counts as I blocked the main thread immediately after calling Unload() but before the stream has been destroyed. Sure enough Unload() waits, so it does appear to require the Message Pump.

I then tried various combinations of delays and even a message pump separate to the main one – get the delay right and all is fine. If the delay is too short, Unload() simply stops and just sits there.
That is why WINWORD.EXE just hangs around and although my application appears to have exited, it is still in the list of processes as the worker thread is still waiting. Manually end the Winword process, Unload() returns and my thread closes.

I have only just discovered this and I am somewhat stunned by what I think my tests are telling me. I guess I made the assumption that the Previewer would have its own message loop if it needed one. Perhaps it doesn’t need one but does need to communicate with the host Window/Control for some reason. I thought we were pushing information but that cannot be the case. So it does appear that some sort of Synchronisation is needed to ensure things shutdown smoothly.

Since altering my application to close down the Preview and then wait before calling PostQuitMessage(), the problem appears to have been completely resolved. I have really hammered it and so far the issue has not resurfaced, fingers-crossed.

If anyone wants to try this out for themselves, note that the larger the file that needs to be unloaded the greater chance that your application will exit before unload has occurred. I did most of my testing with a large doc file (10 meg) and a 60Meg PDF file both of which I previewed across my network. This allowed me to easily replicate the problem at will.

Environment (Window 8, Adobe PDF Reader 11.1)
exceute : ((IPreviewHandler)mCurrentPreviewHandler).DoPreview();
in case of pdf file, it gives exception like : error hresult e_fail has been returned from a call to a com component. It happens onlu in windows 8. It working fines in Windows 7.

The Adobe PDF Preview Handler can be used in this environment – I can confirm that it works under the environment I use (Reader 11.0.3, Windows 8 64-bit). Be sure to check the other comments on this post for additional considerations, in particular mismatches that may occur in relation to architecture (64-bit processes cannot invoke 32-bit preview handlers, and vice versa). Windows 8 also introduces some changes in relation to UAC and permissions; you may experience problems invoking preview handlers if your application is not started with appropriate permissions.

I also had this problem and discovered that yet again we have a bit of a dogs breakfast going on with Windows 8.

The problem specifically with the previewing of the PDF file is that Windows 8 comes with its own PDF reader which is a metro application and DoPreview() fails.

What I then did was install Adobe Reader 9.5 for testing, but I still got the error. Then I went into the Default Programs section in Control Panel and made sure that .PDF was associated with Adobe Reader. This solved the problem.

Note that Windows 8 causes grief for the Outlook 2007 preview facility as well. Yet another example of things not being thought through correctly on WIn8.

I have created a preview handler to dispaly an image and i am capable to display the image on preview pane of window explorer(using compilation in VSC++ and then registering it using regsvr32 fName.dll). it works succesfully in Wexplorer but now i tried to do so in in preview pane of outlook 2010 by attching that file on a mail.

by random search on mail i got some idea that i need to create add-in for my^preview handler and then only it will work on outlook … do you have any idea how to do that ..even i registered my preview handler on the path ( hr = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CURRENT_USER,L”Software\\Microsoft\\Office\\Outlook\\Addins\\AMEPreviewHandler”, 0, KEY_WRITE, &hKey));)

We’re using your sample code in a project of ours, and it works great. We use it to add a preview of word files inside a VSTO solution. Sadly, we then get issues when users click inside the preview (which is a live Word document then), as they cannot get out of the preview anymore (Focus is caught up). Only way to get out of it is to switch to another project, and return to Word.
Have you got any ideas on this issue?

I’m afraid I can’t shed much light on this. If this is a VSTO solution, then I suspect the problem is due to your code running within another of the Office applications, which may process certain window messages (e.g. focus) and not filter them through to child windows. There may well be a solution to this, but I expect it would be complicated and involve a lot of direct calls to the Win32 API.

excellent code. just one thing i noticed was that when you load power point file with auto advance slide settings using timing, the slides doesn’t move and stays on the first slide. is there a work around to solve this problem?

I suspect that the PowerPoint preview handler simply doesn’t support this. You have to remember that these are just previews of the slides, and that the preview handler has been optimised to load quickly and use few resources.

I don’t think so. This looks like RemoteApp to me. Whereas preview handlers simply leverage existing application components that are already installed on your computer, RemoteApp lets you view and control an application running on a different machine without having to install anything on the client side.

My project is a WPF application .
In our project there is requirement like displaying the initial content (or page) of file in freeze mode.The file can be of type .pptx,.pdf,.jpg,.png,.html,.txt,video file….
To achieve this we are using Preview handler implementation which is present in the in this link
While displaying the file it is loading the entire content of file instead of snapshot of first page and also showing the horizonal and vertical scrolls,which are need to my requirement.
Can any one please share your ideas in resolving the below problems
1)How to remove the scrolls of preview handler.
2)How to get the intial content or first page of file in freeze mode.

You will not be able to achieve either of these goals by using preview handlers; each preview handler is different and has complete control over how it displays its content and the user interface it provides. The only functionality exposed to hosts is that which is covered in my article (setting the window size and loading the file or stream).

If you want greater control over document previews, you will have to write your own previewing functionality on a per-format basis (or use other third-party code). The logic required to preview, for example, a PDF is drastically different to what would be required to preview a Word document. There is no ‘one-size-fits-all’ solution.

Thank you so much for such a great work of creating managed API for documents preview. Apparently you have saved so much time of ours who have come to your site in the need and search of incorporating documents preview in the respective projects or for other purpose.

My project is in C#.net and if you can shed some light on the following then would be really helpful.

[1] How do I manage to get preview control to display word documents in their default/standard size? Like when you view word document in the word application itself or using WebBrowser control. The word document display gets distorted if I resize the preview control to full screen. I do know that I can manually set the size of rectangle control, but I want to display the original size of the word document at all times without using any non standard method. The WebBrowser control displays word documents in their original default size but there I have to handle lots of things to display word document as readonly and to hide the toolbar etc..so I do not prefer the WebBrowser control.

[2] I have registered the preview handlers for excel macro enabled – XLSM files but neither preview control nor windows explorer display the preview. Is this extension not supported for the preview?

1. Word 2010 and above now default to the ‘reading view’ for documents which are previewed or opened from an untrusted location. In this mode, you don’t have any control over the zoom amount. In any case, this is determined internally by the Word preview handler and is not part of the API that is exposed.

2. I suspect this relates to security/trust settings, and it may not be possible to change this. Current versions of Word are very paranoid about macro-enabled documents and may be hard-coded not to display previews for them. Try looking in the settings for Word itself to see if you can change this behaviour.

I am using IPreviewHandler to preview office documents such as word, ppt and excel in a c# application. This works great. Now, I would like to preview password protected documents. Is there anyway to provide password to the preview handler for the office documents?

There is nothing in the API that can achieve this; you can only initialise and display preview handlers, all other functionality lives and dies within each specific implementation. Password-protected documents are not designed to be previewed.

If the documents are in the new (current) OpenXML formats, you might be able to use the OpenXML SDK to decode the content (assuming the passwords are known). You can do this on-the-fly, writing the unprotected document to a temporary location, loading it into the preview handler and then removing the temp file at the end of the process.

Thanks for the response. It was useful. But now I got something to say. For excel, if I tried to preview it, I get a popup for entering password. After I enter the password, I am able to preview the excel documents. But for word and PPT, its not happening. I don’t get such popup to enter the password.

If I opened the password protected word document in winword.exe(i,e after entering the password) I am able to preview the same word document using IPreviewHandler, but for PPT its not happening.

Can you please suggest me any interop to use the pluggins winword.exe, excel.exe, powerpnt.exe to enter passwords and then preview it using IPreviewHandler? Or any other Idea? Since, I don’t want to create a copy of password protected document as unprotected document, there should be another way to achieve it. Expecting your valuable comments.

I am using IPreviewHandler to preview office documents. This works great for WORD and Powerpoint documents. however, when I preview excel files it goes in hang state.
I found it has gone in the method ((IPreviewHandler)mCurrentPreviewHandler).DoPreview(); and never comes back .
After checking in the task Manager, I found that EXCEL.exe process keeps on running in the background. The control never returns fromthe DoPreview() function.
The strange thing is that the very same excel opens up pretty well in my test application but not in my main project.
Can you please enlighten me what could be possible reason for such a scenario?
Looking forward to your reply.