Xps printing from windows service

匿名 (未验证) 提交于 2019-12-03 01:29:01

问题:

I'm trying to print XPS documents from a windows service on the .net framework. Since Microsoft does not support printing by using System.Drawing.Printing nor by using System.Printing (WPF), I'm using the native XPSPrint API. This is suggested to me by Aspose in http://www.aspose.com/documentation/.net-components/aspose.words-for-.net/howto-print-a-document-on-a-server-via-the-xpsprint-api.html.

When I try to print an XPS document from a windows service, the result contains strange characters instead of the text I want.

I tried with different printers (including virtual printers like for instance PDFCreator), different users and user-privileges for the service, different xps generators (aspose, word 2007, word 2010), different platforms (windows 7, windows 2008 R2) but all have the same result.

Does anybody knows how to solve this? Any help would be appreciated!

For those who want to try it, I shared some files via:

https://docs.google.com/leaf?id=0B4J93Ly5WzQKNWU2ZjM0MDYtMjFiMi00NzM0LTg4MTgtYjVlNDA5NWQyMTc3&hl=nl

  • document.xps: the XPS document to print
  • document_printed_to_pdfcreator.pdf: the printed document that demonstrates what is going wrong
  • XpsPrintTest.zip: a sample VS2010 solution with the sample code

The sample code for the managed windows service is:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.IO; using System.Threading; using System.Runtime.InteropServices;  namespace PrintXpsService { public partial class XpsPrintService : ServiceBase {     // Change name of printer here     private String f_printerName = "PDFCreator";      // path to some file where logging is done     private String f_logFile = @"C:\temp\testdoc\xps_printing_service_log.txt";      // path to xps file to print     private String f_xpsFile = @"C:\temp\testdoc\document.xps";      public XpsPrintService()     {         InitializeComponent();     }      private void Log(String fmt, params Object[] args)     {         try         {             DateTime now = DateTime.Now;              using (StreamWriter wrt = new StreamWriter(f_logFile, true))             {                 wrt.Write("{0} {1} - ", now.ToShortDateString(), now.ToShortTimeString());                 wrt.WriteLine(fmt, args);             }         }         catch (Exception ex)         {         }     }      protected override void OnStart(string[] args)     {         // uncomment to allow to connect debugger         //int i = 0;         //while (i == 0)         //{         //    if (i == 0)         //    {         //        Thread.Sleep(1000);         //    }         //}          Log("Starting Service");         try         {             Log("Printing xps file {0}", f_xpsFile);              using (Stream stream = new FileStream(f_xpsFile, FileMode.Open, FileAccess.Read))             {                 Log("Starting to print on printer {0}", f_printerName);                 String jobName = f_xpsFile;                 this.Print(stream, jobName);             }             Log("Document printed");         }         catch (Exception ex)         {             Log("Exception during execution: {0}", ex.Message);             Log("  {0}", ex.StackTrace);             Exception inner = ex.InnerException;             while (inner != null)             {                 Log("=== Inner Exception: {0}", inner.Message);                 Log("    {0}", inner.StackTrace);                 inner = inner.InnerException;             }         }     }      protected override void OnStop()     {     }      public void Print(Stream stream, String jobName)     {         String printerName = f_printerName;         IntPtr completionEvent = CreateEvent(IntPtr.Zero, true, false, null);         try         {             IXpsPrintJob job;             IXpsPrintJobStream jobStream;              StartJob(printerName, jobName, completionEvent, out job, out jobStream);             CopyJob(stream, job, jobStream);             WaitForJob(completionEvent, -1);             CheckJobStatus(job);         }         finally         {             if (completionEvent != IntPtr.Zero)                 CloseHandle(completionEvent);         }     }      private void StartJob(String printerName,         String jobName, IntPtr completionEvent,         out IXpsPrintJob job,         out IXpsPrintJobStream jobStream)     {         int result = StartXpsPrintJob(printerName, jobName, null, IntPtr.Zero, completionEvent,             null, 0, out job, out jobStream, IntPtr.Zero);         if (result != 0)             throw new Win32Exception(result);     }       private void CopyJob(Stream stream, IXpsPrintJob job, IXpsPrintJobStream jobStream)     {         try         {             byte[] buff = new byte[4096];             while (true)             {                 uint read = (uint)stream.Read(buff, 0, buff.Length);                 if (read == 0)                     break;                 uint written;                 jobStream.Write(buff, read, out written);                  if (read != written)                     throw new Exception("Failed to copy data to the print job stream.");             }              // Indicate that the entire document has been copied.             jobStream.Close();         }         catch (Exception)         {             // Cancel the job if we had any trouble submitting it.             job.Cancel();             throw;         }     }      private void WaitForJob(IntPtr completionEvent, int timeout)     {         if (timeout  /// This interface definition is HACKED. ///  /// It appears that the IID for IXpsPrintJobStream specified in XpsPrint.h as  /// MIDL_INTERFACE("7a77dc5f-45d6-4dff-9307-d8cb846347ca") is not correct and the RCW cannot return it. /// But the returned object returns the parent ISequentialStream inteface successfully. ///  /// So the hack is that we obtain the ISequentialStream interface but work with it as  /// with the IXpsPrintJobStream interface.  ///  [Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D")]  // This is IID of ISequenatialSteam. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IXpsPrintJobStream {     // ISequentualStream 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(); }  [Guid("5ab89b06-8194-425f-ab3b-d7a96e350161")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IXpsPrintJob {     void Cancel();     void GetJobStatus(out XPS_JOB_STATUS jobStatus); }  [StructLayout(LayoutKind.Sequential)] struct XPS_JOB_STATUS {     public UInt32 jobId;     public Int32 currentDocument;     public Int32 currentPage;     public Int32 currentPageTotal;     public XPS_JOB_COMPLETION completion;     public Int32 jobStatus; // UInt32 };  enum XPS_JOB_COMPLETION {     XPS_JOB_IN_PROGRESS = 0,     XPS_JOB_COMPLETED = 1,     XPS_JOB_CANCELLED = 2,     XPS_JOB_FAILED = 3 }  enum WAIT_RESULT {     WAIT_OBJECT_0 = 0,     WAIT_ABANDONED = 0x80,     WAIT_TIMEOUT = 0x102,     WAIT_FAILED = -1 // 0xFFFFFFFF } } 

Note: some links for more information:

回答1:

I talked with microsoft about this issue and we discovered the problem is related to incorrect font substitution in the printer-spooler. When the printer is set to not spool the documents, they are printed correctly, also from a windows service. Otherwise, all fonts, except arial (and maybe some others), are substituted by another font. In the sample I provided, calibri is substituted by wingdings.

So, they acknowledge this to be a bug but at the moment they will not resolve it. It will depend on how many people will suffer from this bug in order for them to decide whether are not they are willing to fix it...



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!