2011-05-25 2 views
9

J'essaie d'imprimer des documents XPS à partir d'un service Windows sur le framework .net. Microsoft ne prenant pas en charge l'impression en utilisant System.Drawing.Printing ni en utilisant System.Printing (WPF), j'utilise l'API XPSPrint native. Ceci est suggéré par Aspose dans http://www.aspose.com/documentation/.net-components/aspose.words-for-.net/howto-print-a-document-on-a-server-via-the-xpsprint-api.html.Impression Xps à partir du service Windows

Lorsque j'essaie d'imprimer un document XPS à partir d'un service Windows, le résultat contient des caractères étranges au lieu du texte que je veux. J'ai essayé avec différentes imprimantes (y compris des imprimantes virtuelles comme par exemple PDFCreator), différents utilisateurs et privilèges d'utilisateur pour le service, différents générateurs xps (aspose, word 2007, word 2010), différentes plateformes (windows 7, windows 2008 R2) mais tous ont le même résultat.

Est-ce que quelqu'un sait comment résoudre ce problème? Toute aide serait appréciée!

Pour ceux qui veulent essayer, j'ai partagé des fichiers via:

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

  • document.xps: le document XPS pour imprimer
  • document_printed_to_pdfcreator.pdf: le document imprimé qui démontre ce qui ne va pas
  • XpsPrintTest.zip: un exemple de solution VS2010 avec l'exemple de code

L'exemple de code pour le service Windows géré est:

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 
} 
} 

Note: quelques liens pour plus d'informations:

+0

J'ai le même problème, mais pas de solution. –

+0

Essayez d'imprimer sur l'imprimante "Microsoft XPS Document Writer". Cela devrait vous donner un document xps que vous pouvez ouvrir et comparer à votre document source. Cela peut donner un indice sur ce qui n'a pas fonctionné. – Jon

+0

@Jon Merci pour la suggestion. Je l'ai essayé et malheureusement (ou heureusement, dépend de votre point de vue :) le document XPS généré par l'imprimante xps est correct. Pas vraiment sûr de ce que je peux en conclure ... – Steven

Répondre

5

J'ai parlé à Microsoft de ce problème et nous avons découvert que le problème est lié à une substitution de polices incorrecte dans le spouleur d'imprimante. Lorsque l'imprimante est configurée pour ne pas spouler les documents, ils sont imprimés correctement, également à partir d'un service Windows. Sinon, toutes les polices, à l'exception de arial (et peut-être d'autres), sont remplacées par une autre police. Dans l'échantillon que j'ai fourni, calibri est remplacé par wingdings. Donc, ils reconnaissent que c'est un bug, mais pour le moment ils ne le résoudront pas. Cela dépendra du nombre de personnes qui souffriront de ce bug afin qu'ils puissent décider s'ils ne veulent pas le réparer ...

+0

Même problème, et désactivation du bobinage travaillé. Pas le temps d'enquêter ou de faire un Connect maintenant. – Will

+1

même problème. le spoulage n'a cependant pas corrigé le problème –

+0

@Steven savez-vous si vous pouvez désactiver le "spouling d'impression" directement dans le ticket d'impression XPS? – Juri

Questions connexes