2011-05-25 331 views
9

我試圖從.NET服務的.NET框架中打印XPS文檔。由於Microsoft不支持使用System.Drawing.Printing進行打印,也不支持使用System.Printing(WPF),因此我使用本機XPSPrint API。 這是Aspose在http://www.aspose.com/documentation/.net-components/aspose.words-for-.net/howto-print-a-document-on-a-server-via-the-xpsprint-api.html給我推薦的。從Windows服務的XPS打印

當我嘗試從Windows服務打印XPS文檔時,結果包含奇怪的字符而不是我想要的文本。 (包括虛擬打印機,如PDFCreator),不同的用戶和用戶權限的服務,不同的XPS生成器(aspose,word 2007,word 2010),不同的平臺(Windows 7,Windows 2008 R2)但都有相同的結果。

有誰知道如何解決這個問題?任何幫助,將不勝感激!

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

  • document.xps:

    對於那些誰想要嘗試它,我通過共享一些文件的XPS文檔打印

  • document_printed_to_pdfcreator.pdf:打印的文檔,演示出了什麼問題
  • XpsPrintTest.zip:示例代碼示例VS2010解決方案

用於管理窗口服務的示例代碼:

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 < 0) 
      timeout = -1; 

     switch (WaitForSingleObject(completionEvent, timeout)) 
     { 
      case WAIT_RESULT.WAIT_OBJECT_0: 
       // Expected result, do nothing. 
       break; 

      case WAIT_RESULT.WAIT_TIMEOUT: 
       // timeout expired 
       throw new Exception("Timeout expired"); 

      case WAIT_RESULT.WAIT_FAILED: 
       throw new Exception("Wait for the job to complete failed"); 

      default: 
       throw new Exception("Unexpected result when waiting for the print job."); 
     } 
    } 

    private void CheckJobStatus(IXpsPrintJob job) 
    { 
     XPS_JOB_STATUS jobStatus; 
     job.GetJobStatus(out jobStatus); 
     switch (jobStatus.completion) 
     { 
      case XPS_JOB_COMPLETION.XPS_JOB_COMPLETED: 
       // Expected result, do nothing. 
       break; 
      case XPS_JOB_COMPLETION.XPS_JOB_IN_PROGRESS: 
       // expected, do nothing, can occur when printer is paused 
       break; 
      case XPS_JOB_COMPLETION.XPS_JOB_FAILED: 
       throw new Win32Exception(jobStatus.jobStatus); 
      default: 
       throw new Exception("Unexpected print job status."); 
     } 
    } 

    [DllImport("XpsPrint.dll", EntryPoint = "StartXpsPrintJob")] 
    private static extern int StartXpsPrintJob(
     [MarshalAs(UnmanagedType.LPWStr)] String printerName, 
     [MarshalAs(UnmanagedType.LPWStr)] String jobName, 
     [MarshalAs(UnmanagedType.LPWStr)] String outputFileName, 
     IntPtr progressEvent, // HANDLE 
     IntPtr completionEvent, // HANDLE 
     [MarshalAs(UnmanagedType.LPArray)] byte[] printablePagesOn, 
     UInt32 printablePagesOnCount, 
     out IXpsPrintJob xpsPrintJob, 
     out IXpsPrintJobStream documentStream, 
     IntPtr printTicketStream); // This is actually "out IXpsPrintJobStream", but we don't use it and just want to pass null, hence IntPtr. 

    [DllImport("Kernel32.dll", SetLastError = true)] 
    private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); 

    [DllImport("Kernel32.dll", SetLastError = true, ExactSpelling = true)] 
    private static extern WAIT_RESULT WaitForSingleObject(IntPtr handle, Int32 milliseconds); 

    [DllImport("Kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool CloseHandle(IntPtr hObject); 
} 

/// <summary> 
/// 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. 
/// </summary> 
[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 
} 
} 

注意:有關詳細信息的一些鏈接:

+0

我有同樣的問題,但沒有解決方法。 – 2011-05-25 15:55:07

+0

嘗試打印到「Microsoft XPS Document Writer」打印機。這應該會給你一個xps文檔,你可以打開它並與源文檔進行比較。這可能暗示出了什麼問題。 – Jon 2011-05-26 21:57:05

+0

@Jon感謝您的建議。我試過了,不幸的是(或者幸運的是,取決於你的觀點:)由XPS打印機生成的XPS文檔是正確的。不太確定我能從中得出什麼結論...... – Steven 2011-05-27 07:49:45

回答

5

我跟微軟討論過這個問題,並且我們發現這個問題與打印機假脫機程序中不正確的字體替換有關。當打印機設置爲不打印文檔時,它們也可以通過Windows服務正確打印。否則,除了宋體(也可能是其他字體)之外的所有字體都會被另一種字體替換。在我提供的樣本中,calibri被wingdings取代。

所以,他們承認這是一個錯誤,但目前他們不會解決它。這將取決於有多少人會受到這個bug的影響,以便他們決定是否他們不願意修復它...

+0

同樣的問題,並關閉假脫機工作。沒時間現在調查或做一個連接。 – Will 2011-08-29 21:01:58

+1

同樣的問題。假脫機但是沒有解決問題 – 2011-09-14 20:06:30

+0

@Steven您是否碰巧知道您是否可以直接在XPS打印工單中禁用「打印假脫機」功能? – Juri 2014-02-10 19:23:00