Created
December 30, 2011 20:50
-
-
Save bgrainger/1541424 to your computer and use it in GitHub Desktop.
Use XPS Print API from managed code -- see http://code.logos.com/blog/2011/12/printing-from-net35-in-windows7.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal static class NativeMethods | |
{ | |
[DllImport("XpsPrint.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] | |
public static extern int StartXpsPrintJob(string printerName, string jobName, string outputFileName, IntPtr progressEvent, SafeWaitHandle completionEvent, | |
[MarshalAs(UnmanagedType.LPArray)] byte[] printablePagesOn, int printablePagesOnCount, out IXpsPrintJob xpsPrintJob, out IXpsPrintJobStream documentStream, out IXpsPrintJobStream printTicketStream); | |
} | |
[ComImport, Guid("E974D26D-3D9B-4D47-88CC-3872F2DC3585"), ClassInterface(ClassInterfaceType.None)] | |
internal class XpsOMObjectFactory | |
{ | |
} | |
[ComImport, Guid("F9B2A685-A50D-4FC2-B764-B56E093EA0CA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | |
internal interface IXpsOMObjectFactory | |
{ | |
void CreatePackage(); | |
[return: MarshalAs(UnmanagedType.Interface)] | |
IXpsOMPackage CreatePackageFromFile([MarshalAs(UnmanagedType.LPWStr)] string filename, bool reuseObjects); | |
void CreatePackageFromStream(); | |
void CreateStoryFragmentsResource(); | |
void CreateDocumentStructureResource(); | |
void CreateSignatureBlockResource(); | |
void CreateRemoteDictionaryResource(); | |
void CreateRemoteDictionaryResourceFromStream(); | |
void CreatePartResources(); | |
void CreateDocumentSequence(); | |
void CreateDocument(); | |
void CreatePageReference(); | |
void CreatePage(); | |
void CreatePageFromStream(); | |
void CreateCanvas(); | |
void CreateGlyphs(); | |
void CreatePath(); | |
void CreateGeometry(); | |
void CreateGeometryFigure(); | |
void CreateMatrixTransform(); | |
void CreateSolidColorBrush(); | |
void CreateColorProfileResource(); | |
void CreateImageBrush(); | |
void CreateVisualBrush(); | |
void CreateImageResource(); | |
void CreatePrintTicketResource(); | |
void CreateFontResource(); | |
void CreateGradientStop(); | |
void CreateLinearGradientBrush(); | |
void CreateRadialGradientBrush(); | |
void CreateCoreProperties(); | |
void CreateDictionary(); | |
void CreatePartUriCollection(); | |
void CreatePackageWriterOnFile(); | |
void CreatePackageWriterOnStream(); | |
void CreatePartUri(); | |
void CreateReadOnlyStreamOnFile(); | |
} | |
[ComImport, Guid("18C3DF65-81E1-4674-91DC-FC452F5A416F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | |
internal interface IXpsOMPackage | |
{ | |
void GetDocumentSequence(); | |
void SetDocumentSequence(); | |
void GetCoreProperties(); | |
void SetCoreProperties(); | |
void GetDiscardControlPartName(); | |
void SetDiscardControlPartName(); | |
void GetThumbnailResource(); | |
void SetThumbnailResource(); | |
void WriteToFile(); | |
void WriteToStream(IXpsPrintJobStream stream, bool optimizeMarkupSize); | |
}; | |
// NOTE: It appears that the IID for IXpsPrintJobStream specified in XpsPrint.h -- | |
// MIDL_INTERFACE("7a77dc5f-45d6-4dff-9307-d8cb846347ca") -- is not correct, or the object | |
// doesn't implement QueryInterface correctly. However, we can QI for ISequentialStream and | |
// successfully (at least in Windows 7 SP1 x86) call the Close method as if it existed on that | |
// interface. | |
// That is, we obtain the ISequentialStream interface, but work with it as the IXpsPrintJobStream interface. | |
// Thanks to http://stackoverflow.com/questions/6123507/xps-printing-from-windows-service for this tip. | |
[ComImport, Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | |
internal interface IXpsPrintJobStream | |
{ | |
// ISequentialStream methods | |
void Read([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbRead); | |
void Write([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbWritten); | |
// IXpsPrintJobStream methods | |
void Close(); | |
} | |
[ComImport, Guid("5AB89B06-8194-425F-AB3B-D7A96E350161"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | |
internal interface IXpsPrintJob | |
{ | |
void Cancel(); | |
IntPtr GetJobStatus(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Prints the specified XPS document to a printer using the native XPS Print API. | |
/// </summary> | |
/// <param name="xpsFilePath">The path to the XPS document.</param> | |
/// <param name="printerName">The printer name.</param> | |
/// <param name="printTicket">A PrintTicket with settings for this print job.</param> | |
/// <returns><c>true</c> if the document was successfully printed; otherwise, <c>false</c>.</returns> | |
/// <remarks>This method should be called from a background thread.</remarks> | |
public bool Print(string xpsFilePath, string printerName, PrintTicket printTicket) | |
{ | |
// try to create the XPS Object Model factory (only available on Windows 7 and Vista with the Platform Update) | |
IXpsOMObjectFactory xpsFactory = null; | |
try | |
{ | |
xpsFactory = (IXpsOMObjectFactory)new XpsOMObjectFactory(); | |
} | |
catch (COMException) | |
{ | |
// OS doesn't support XPS Document API | |
return false; | |
} | |
bool success = false; | |
IXpsOMPackage package = null; | |
try | |
{ | |
// load the saved document as a native XpsOMPackage | |
package = xpsFactory.CreatePackageFromFile(xpsFilePath, false); | |
using (ManualResetEvent handle = new ManualResetEvent(false)) | |
{ | |
// attempt to start the print job | |
IXpsPrintJob printJob; | |
IXpsPrintJobStream docStream, ticketStream; | |
int hresult = NativeMethods.StartXpsPrintJob(printerName, jobTitle, null, IntPtr.Zero, handle.SafeWaitHandle, null, 0, out printJob, out docStream, out ticketStream); | |
// check for success (NOTE: checking HRESULT value directly instead of calling Marshal.ThrowExceptionForHR to avoid proliferation of 'catch' blocks) | |
if (hresult >= 0) | |
{ | |
// write the current printer settings to the print ticket stream | |
byte[] ticketData = printTicket.GetXmlStream().ToArray(); | |
uint bytesWritten; | |
ticketStream.Write(ticketData, (uint) ticketData.Length, out bytesWritten); | |
ticketStream.Close(); | |
// write the XPS package to the document stream | |
package.WriteToStream(docStream, false); | |
docStream.Close(); | |
// wait for printing to finish | |
handle.WaitOne(); | |
success = true; | |
} | |
} | |
} | |
catch (COMException) | |
{ | |
// printing failed | |
} | |
catch (DllNotFoundException) | |
{ | |
// OS doesn't support XPS Print API | |
} | |
catch (EntryPointNotFoundException) | |
{ | |
// OS doesn't support XPS Print API | |
} | |
// force the XPS package to be released, so that the temporary file can be deleted | |
if (package != null) | |
Marshal.FinalReleaseComObject(package); | |
return success; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment