The WebBrowser control is a pretty cool thing. You can use it to host many different document types allowing the user to work with a specific type of document in its most common user interface, at the same time the WebBrowser control frees the developer from writing much of the interop code that would be needed otherwise. And that is always a plus!
However as with any kind of inter-operation between disparate technologies you run into hiccups. A good example of this is what happens when you use the WebBrowser control as a container for AcroRd32.exe to view PDF documents. To understand this ‘hiccup’ we can look to Adobe knowledge base for some guidance.
Adobe Acrobat and Adobe Reader are designed to continue running for a few minutes after you close the browser window in which you viewed PDF files.
If you are interested in a more detailed description take take a gander at this KB.
Adobes ‘Design’ decisions present several challenges when working with PDF documents in a WebBrowser control. The following use case illustrates it best.
- User searches for a document.
- User views document (Presumably in a WebBrowser control).
- User closes document.
- User attempts to delete the document after closing it.
A issue arises when the user attempts to delete the document upon closing it. Because AcroRd32.exe will hang on to your precious PDF document for ‘few minutes’ upon closure any attempt to delete said document will result in the following error.
The process cannot access the file ‘YourPdf.pdf’ because it is being used by another process. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
This is a problem. Adobe is holding our document hostage. This aggression will not stand man! Outside of killing the AcroRd32 process (an option that results in all open PDF documents being closed) we have very little recourse against Adobe’s design decision. Getting around this little hiccup ends up being pretty simple and involves always loading a ‘working’ version of the file. Or simply stated every document you load with the WebBrowser control should be a copy of actual document you want to work with. So how do we manage that copy? I suppose there are several ways to do this. However one I found particularly interesting was to use the use the TempFileCollection in the System.CodeDom.Compiler namespace. This was interesting to me for one major reason, when adding a file to this collection you can supply a flag to indicated that it should be removed upon garbage collection. This is perfect when you truly want a working file and you want it to be gone when you are done with it. Lets take a look at some code that might do this.
<Window x:Class="PDFSample.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> </Grid.ColumnDefinitions> <WebBrowser x:Name="wbPdfViewer" Grid.Column="0" /> </Grid> </Window>
and the code behind….
using System;
using System.CodeDom.Compiler;
using System.Windows;
namespace PDFSample
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
#region Private Data Members
private readonly TempFileCollection mTempFile = new TempFileCollection();
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="Window1"/> class.
/// </summary>
public Window1()
{
InitializeComponent();
wbPdfViewer.Source = new Uri(GetWorkingDocumentPath());
}
#endregion
/// <summary>
/// Gets the working document path.
/// </summary>
/// <returns></returns>
private string GetWorkingDocumentPath()
{
string workingFile = string.Concat(System.IO.Path.GetTempPath(), "MySampleApp_", Guid.NewGuid(),".pdf");
System.IO.File.Copy(@"C:\Unity-v1p2-October08.pdf", workingFile);
mTempFile.AddFile(workingFile, false);
return workingFile;
}
}
}
Enjoy!




