UnauthorizedAccessException in PDF Preview Handler

For my article on preview handlers in the January 2007 issue of MSDN Magazine, I wrote a sample preview handler for previewing PDF files.  This allows you to view PDF files in Outlook and in the Vista shell just as you would other document types for which there are built-in preview handlers, like for Word and Excel.

System.UnauthorizedAccessException: Access to the path 'C:\Users\yourusername\AppData\Local\Temp\3373c62fe9214489828d0ee72cd53d30.PDF' is denied.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at MsdnMag.FileBasedPreviewHandlerControl.MakeTemporaryCopy(FileInfo file)
   at MsdnMag.PdfPreviewHandler.PdfPreviewHandlerControl.Load(FileInfo file)
   at MsdnMag.FileBasedPreviewHandler.Load(PreviewHandlerControl c)
   at MsdnMag.PreviewHandler.<MsdnMag.IPreviewHandler.DoPreview>b__2()

What's going on here?

This is due to a new integrity mechanism/security feature in Windows Vista that restricts write access to securable objects like processes, files, and registry keys with higher integrity levels than the current process.  By default, the prevhost.exe process hosting a preview handler runs as a low-integrity process, which means it doesn't have write access to the current users profile.  The PDF preview handler I provided is attempting to write to the temp directory, and as a result it's getting an UnauthorizedAccessException.

This begs the question: why does a preview handler need to write anything?  Isn't it just reading data from disk and displaying it?  Yes, that should be the case.  However, I wrote the preview handler this way to work around a different problem.  As mentioned in the article in MSDN Magazine, when opening files to be displayed, preview handlers should open them to be sharable such that another process could read from or write to the same file, or even delete that file, while it's being used by the preview handler.  In the case of my preview handler for PDF files, I'm using the Acrobat Reader ActiveX control to display the PDF files, and in doing so I give up some control over how it loads the file.  Unfortunately, while the PDF file is being loaded by the ActiveX control, it is not shared, preventing other processes such as the shell itself from working with the file.  This means that if a user tries to delete the PDF while it's being rendered initially, they'll receive an error saying that the file is currently in use by another process.  This isn't usually an issue if the file is small or if it's local, but if the PDF resides on a remote server and takes a while to download and/or load, it could be locked for an unacceptable amount of time.

To work around this, I first copy the PDF from wherever it resides to the user's local temp folder, and then open the new file.  The copy operation is performed by opening the file for all sharing operations, and it doesn't matter if the preview handler temporarily locks the temp copy. Unfortunately, this operation also causes the exception in question, which you can see by looking at the stack trace. The exception comes from a method MakeTemporaryCopy, and the exception is due to not being able to write to the user's %temp% directory.

There are two quick solutions to this problem:
1) Modify the PDF preview handler by simply removing the line of code that calls MakeTemporaryCopy to create the copy and operate on it instead of on the original.
2) Turn off the low-integrity status of the prevhost.exe process.

The first solution is self-explanatory and requires a recompile and reinstallation of the assembly into the GAC.

The second solution requires adding a value to the registry.  Specifically, create a DWORD value on HKEY_CLASSES_ROOT\CLSID\{574fffaa-17f6-44b1-a1b4-177ab5900a51} named DisableLowILProcessIsolation and set it to 1.  This will cause all new prevhost processes for this PDF preview handler to be created as normal integrity, which means they will have the ability to write to the user's %temp% directory.  If you look in the PreviewHandler.cs file, and specifically at the registration code, you'll see that there's a line of code in there that sets this value, but in the download prior to today, it's commented out (I put up a new version of this code download with the line uncommented).  If you want all of your managed preview handlers to run in a normal-integrity process, just uncomment this line, recompile, and reinstall.  That should fix the error.