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.
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.
private Guid GetPreviewHandlerGUID(string filename) { RegistryKey ext = Registry.ClassesRoot.OpenSubKey(Path.GetExtension(filename)); if (ext != null) { RegistryKey test = ext.OpenSubKey("shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}"); if (test != null) return new Guid(Convert.ToString(test.GetValue(null))); string className = Convert.ToString(ext.GetValue(null)); if (className != null) { test = Registry.ClassesRoot.OpenSubKey(className + "\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}"); if (test != null) return new Guid(Convert.ToString(test.GetValue(null))); } } return Guid.Empty; }
- Using reflection (since there is no assembly or type library we can use), create an instance of the preview handler (using Type.FromCLSID() and Activator.CreateInstance()).
Type comType = Type.GetTypeFromCLSID(mCurrentPreviewHandlerGUID); mCurrentPreviewHandler = Activator.CreateInstance(comType);
- Depending on the type, initialise the preview handler with a filename (IInitializeWithFile) or a stream containing the content to preview (IInitializeWithStream).
if (mCurrentPreviewHandler is IInitializeWithFile) { ((IInitializeWithFile)mCurrentPreviewHandler).Initialize(filename, 0); } else if (mCurrentPreviewHandler is IInitializeWithStream) { mCurrentPreviewHandlerStream = File.Open(filename, FileMode.Open); StreamWrapper stream = new StreamWrapper(mCurrentPreviewHandlerStream); ((IInitializeWithStream)mCurrentPreviewHandler).Initialize(stream, 0); }
- Bind the preview handler to the control you want to host it in (you can limit it to a region within the control’s bounds) and activate it.
Rectangle r = ClientRectangle; ((IPreviewHandler)mCurrentPreviewHandler).SetWindow(Handle, ref r); ((IPreviewHandler)mCurrentPreviewHandler).DoPreview();
There are some hurdles involved in getting the COM types into .NET:
- 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.





Hi Bradley,
Thanks for this. Have you figured out a problem with TXT Previewer, i still can’t solve it.
Also I would some small code at the end of GetPreviewHandlerGUID method, kind of a fallback (work for all the files that have text value of PercievedType):
else
{
string PercievedType = Convert.ToString(extension.GetValue(“PerceivedType”));
if (PercievedType != null)
{
string PercievedTypePreviewHandlerCLSID = string.Format(@”SystemFileAssociations\{0}\shellex\{8895b1c6-b41f-4c1c-a562-0d564250836f}”, PercievedType);
registryKey = Registry.ClassesRoot.OpenSubKey(PercievedTypePreviewHandlerCLSID);
if (registryKey != null)
return new Guid(Convert.ToString(registryKey.GetValue(null)));
}
}
Best Regards,
Marko
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.
I got the TXT preview handler to work by compiling the code as x64 in VS 2010. I’m on a 64bit system.
Thank you for your reply.
It works when app is build in x64 mode, but not x86, that’s all I could get.
Thanks again for your sample, it helped a lot.
Best regards,
Marko
Thanks for the great post.
Is there a way to highlight specific words, using preview handler?
Unfortunately that’s not possible; the preview handler API only exposes the functionality to load content into the handler, not to interact with it in any way.
Hi,
By default, when I drop your previewHost component on a WinForm, I can open word files with it but can’t open pdf files for example.
Can you tell me how can I register additional handlers for another file types which I need in my App, to be opened with this component?
Thanks,
Vedran
Very nice post.
Just one quick remark: preview handlers can also be registered per user, ie: the guid discovery code should check HKCU as well (don’t know if it’s documented anywhere though…)
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
Aha, I wondered if there was something in the Windows SDK to deal with this – thanks for your contribution, should save a lot of time and effort!
hello, i’m testing this source code, but only works fine with words files, you can give a complete source code??
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.
Found some issues (Windows 7 x64, Office 2010, VS 2010, .Net 4.0):
- OnResize need SetRect(ref r) to be called instead of SetWindow(…);
- Dispose(…) raises “COM object that has been separated from its underlying RCW cannot be used.”. Next helps:
if (Marshal.AreComObjectsAvailableForCleanup()) { UnloadPreviewHandler(); }
Thanks, Alex, I appreciate your feedback
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.
Great job!
Any idea / hint how todo that in Delphi (win32, not .NET)
Should be even more straight forward (directly using COM)
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.
So anyone have any ideas – I am on Windows 7 / Office 2010 / VS 2010 / tried both .net 4 and .net 3.5
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.
I seem to have solved this problem by disposing the previewhandler in the “closing” event of the form – i.e. not leaving it to the general component dispose – this seems to shut down word correctly:
public Preview_Form()
{
InitializeComponent();
this.FormClosing += new FormClosingEventHandler(Preview_FormClosing);
}
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.
Anyhow put it here in case someone else has this issue.
when i try to preview sometimes i get following error:
Cannot create a top-level child window. (Exception from HRESULT: 0x8007057E)
can u tell me why is it so???
Thanks
hey,
I want to bind this previewhandler to a wpf usercontrol any idea hw to do it????
You’ll have to use the WindowsFormsHost element; it allows you to embed any Windows Forms control in a WPF application. Have a look at this article: http://msdn.microsoft.com/en-us/library/ms751761.aspx
(You cannot directly host preview handlers in WPF because the API does not expose the window handles of its controls.)
HI Smith,
First of all thank you very much for a great article on preview handlers.
I was searching for a long time for this type of previewer to use with my C# application. Finally got from you site.
By the way I am getting an error when I resize the form where this handler is placed.
This is only happen when the handler is loaded with MS Office File.
************** Exception Text **************
System.Runtime.InteropServices.COMException (0×80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
at IPreviewHandler.SetWindow(IntPtr hwnd, Rectangle& rect)
at PreviewHandlerHost.OnResize(EventArgs e) in C:\Users\vadelk\Documents\Visual Studio 2010\Projects\WindowsFormsApplication1\WindowsFormsApplication1\PreviewHandler.cs:line 126
at System.Windows.Forms.Control.OnSizeChanged(EventArgs e)
at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight)
at System.Windows.Forms.Control.UpdateBounds()
at System.Windows.Forms.Control.WmWindowPosChanged(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Do You Have any idea on above error?
[...] [2] http://www.brad-smith.info/blog/archives/79 [...]