Introduction
The Windows XPS Document writer provides a handy tool for generating XPS documents. However, it is
limited in its usage for projects, as it requires user intervention to enter a destination file name.
This article provides a ready-to-use class that allows the production of an XPS document directly from code.
Background
Our project required XPS versions of documents contained within our ECM to be generated on a server-side process.
Optimally, this process would be a Windows service. The Windows XPS Document Writer, as it stands, was
not suitable due to its requiring user input for each document that was to be rendered.
A Google of this issue came up with the same solution just about everywhere - use the XPS Passthrough print driver,
freely available from Frogmore. This was great, apart from the fact that none of the solutions describe how
to use this driver from code. This article demonstrates the solution to that problem.
Using the Code
This code is centered around creating a virtual printer, and then letting this virtual printer know the location of the
required output. It creates the output port on the "Local Port" monitor, using the extremely poorly documented "AddPortEx"
API. Although this function has been deprecated, the Local Port monitor has not changed since the days of NT3, and is still exposed.
If it does happen to go away, then this API call may need to be changed to use a WMI object.
The VirtualPrinter
class can be used either as a single use output device, or can be persisted and have the output port
(XPS file location) changed for individual print jobs. Note that the disposal function of the Virtual printer will destroy
a virtual printer if and only if that instance created it. It will also destroy the operating system's knowledge of any ports
it created.
To use the code, simply create an instance of the VirtualPrinter
class, passing it a printer name and a filename to send the
output to. Use this printer name to direct output to the specified XPS file. To create another XPS document, simply call the
SetPort
method of the Virtual printer, passing it the new file name.
using (VirtualPrinter printer = new VirtualPrinter("MyXPSPrinter", "File1.xps")) {
PrintReport1To("MyXPSPrinter");
printer.SetPort("File2.xps");
PrintReport2To("MyXPSPrinter");
}
The "using" class above will cause the Dispose
method of the VirtualPrinter
to be called, which will
delete "MyXPSPrinter
" from the operating system's printers, as will as removing "File1.xps" and "File2.xps"
from the operating system's available ports.
The sample code included demonstrates two different uses of the VirtualPrinter
class. Firstly, it
demonstrates initiating a print job, and printing some sort of output directly from the code. This uses
the standard System.Printing
functionality provided in the .NET framework. It should be noted that this
functionality is not supported in a Windows Service - to print from a service, code would need to access
the low level windows API's via PInvoke.
The second sample demonstrates printing of a file on disk using the default file association. First, it
attempts to find a "printto" verb for the specified file type, and if it exists, then the command is issued
to print to the specified virtual printer. Otherwise, it sets the default printer to the virtual printer, and
executes the "print" verb for the specified file.