Skip to content

Instantly share code, notes, and snippets.

@bgrainger
Created December 30, 2011 20:50
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save bgrainger/1541424 to your computer and use it in GitHub Desktop.
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();
};
/// <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