2014-12-10 25 views
0

我試圖創造通過網絡接收打印作業C#中的「虛擬打印機」的應用,分析了某些信息的原始打印數據,然後將文檔保存到數據庫中。以下類的修改版本正在爲postscript打印作業(它將輸入數據保存到有效的.prn文件,就像打印機被設置爲打印到「FILE:」端口一樣)。當我嘗試捕獲時。來自Microsoft XPS Document Writer的XPS文檔雖然無法打開文檔。如果擴展名已重命名,則有效的XPS文件也應該是有效的ZIP文件,並且這也不起作用。當我將相同的文檔打印到FILE:端口,然後打印到我的應用程序時,並且我比較了Notepad ++中的結果時,數據長度有5個字符的差異,但看起來相同(它不是明文的,所以它是很難看,但前幾個字符和最後幾個字符看起來是相同的)。該文件保存「正常」的方式工作正常,但我的代碼生成的文件沒有。接收的XPS文件通過TcpClient的腐敗

更廣泛地說,我試圖通過TCP端口接收任意數據,並將其寫入文件。我的解決方案是「關閉」,但無法正常工作。我不知道XPS使用的是什麼類型的編碼,但是我使用ASCII作爲postscript,並且我已經嘗試過使用ASCII和UTF8編碼這個XPS版本。

任何幫助,非常感謝!這裏是我的代碼的相關部分:

class XPSListener 
 
    { 
 
     private TcpListener tcpListener; 
 
     private Thread listenThread; 
 
     private string instanceName = ""; 
 
     private string fileShare = (Settings.Default.SharedPath.Substring(Settings.Default.SharedPath.Length - 1) == @"\") ? Settings.Default.SharedPath : Settings.Default.SharedPath + @"\"; // use SharedPath setting value - append backslash if it isn't already there. 
 

 
     public XPSListener(string initInstanceName, Int32 initPort) 
 
     { 
 
      this.instanceName = initInstanceName; 
 
      this.tcpListener = new TcpListener(IPAddress.Any, initPort); 
 
      this.listenThread = new Thread(new ThreadStart(ListenForClients)); 
 
      this.listenThread.Start(); 
 
     } 
 

 
     private void ListenForClients() 
 
     { 
 
      try 
 
      { 
 
       this.tcpListener.Start(); 
 
      } 
 
      catch (Exception e) 
 
      { 
 
       MessageBox.Show("Socket Error 1 - " + e.StackTrace); 
 
      } 
 

 
      while (true) 
 
      { 
 
       //blocks until a client has connected to the server 
 
       TcpClient client = this.tcpListener.AcceptTcpClient(); 
 

 
       //create a thread to handle communication with connected client 
 
       Thread clientThread = new Thread(new ParameterizedThreadStart(AcceptXPSData)); 
 
       clientThread.Start(client); 
 
      } 
 
     } 
 

 
     private void AcceptXPSData(object client) 
 
     { 
 
      TcpClient tcpClient = (TcpClient)client; 
 
      NetworkStream clientStream = tcpClient.GetStream(); 
 
      string tempFilePath = fileShare + "XPStemp_" + instanceName + ".oxps"; 
 

 
      byte[] message = new byte[65536]; 
 
      int bytesRead; 
 
      string input; 
 

 
      while (true) 
 
      { 
 
       bytesRead = 0; 
 

 
       try 
 
       { 
 
        //blocks until a client sends a message 
 
        bytesRead = clientStream.Read(message, 0, 65536); 
 
        Debug.WriteLine("Bytes read: " + bytesRead.ToString()); 
 
       } 
 
       catch 
 
       { 
 
        //a socket error has occured 
 
        break; 
 
       } 
 

 
       if (bytesRead == 0) 
 
       { 
 
        //the client has disconnected from the server 
 
        break; 
 
       } 
 

 
       //message has successfully been received 
 
       if (instanceName != "DontPrint") 
 
       { 
 
        Debug.WriteLine(instanceName + " Receiving Data"); 
 
        //ASCIIEncoding encoder = new ASCIIEncoding(); 
 
        UTF8Encoding encoder = new UTF8Encoding(); 
 

 
        using (FileStream fs = new FileStream(tempFilePath, FileMode.Append, FileAccess.Write)) 
 
        { 
 
         using (StreamWriter sw = new StreamWriter(fs)) 
 
         { 
 
          input = encoder.GetString(message, 0, bytesRead); 
 
          sw.Write(input); 
 
          // first capture this input and write it to an xps file. This file can be converted to PDF at a later time by Ghostscript 
 
          // but we will still have access to the temp file for parsing purposes. 
 
         } 
 
        } 
 
       } 
 
       
 
       
 
      } 
 

 
      tcpClient.Close(); 
 

 
      // processXPS(); 
 
     }

+0

@PeterDuniho - 我使用微軟XPS Document Writer的打印驅動程序,只是想使用不同的端口,以將結果發送到我的應用程序,而不是提示用戶保存到硬盤驅動器。其原因是自動化文件命名和存儲的方式。如果每次用戶輸入錯誤時文件都會丟失,這將是一場噩夢。 – 2014-12-10 22:57:03

+0

那麼,您可以使用Visual Studio中的二進制編輯器來查看文件之間的_precise_差異。我仍然不明白'TcpClient'是如何涉及到的,但它不會破壞你的數據,我可以告訴你很多。你沒有正確地使用它 - 沒有理由相信只要對Read()進行一次調用就可以接收到整個文件的數據。重新編碼:如果您只是將數據寫入文件,則根本不要解釋它......只需將字節直接寫入文件即可。 XML(以及XPS)使用UTF8,但在這裏看起來並不重要。 – 2014-12-10 23:06:04

+0

我不是責怪TCPClient,我只是有一個錯誤。 Read()在一個循環中 - 它填充一個緩衝區,然後附加到文件,直到沒有更多數據進入爲止。代碼的調試結果爲「Bytes Read:65536 Bytes Read:65536 Bytes Read:45888 Bytes Read:0 「 - 將緩衝區填充兩次,然後讀取剩餘的45888字節。這總計172.8 kb,這是我「打印以正常方式打印」時獲得的文件的確切大小,但有趣的是,我的程序生成的文件爲310kb,而不是調試所指示的173 KB。二進制編輯器顯示「類似的數據」,但在我的很多額外的垃圾。 – 2014-12-10 23:39:31

回答

1

您的代碼至少有兩個問題,其中一個幾乎可以肯定你寫的文件是不正確的原因:

  1. 您不斷重新打開您要寫入的文件,而不是隻打開一次。
  2. 您正在將您收到的字節解釋爲文本,然後對它們進行重新編碼。

第一個問題是更多的效率/文件鎖定問題,而不是正確性問題。但第二個是一個大問題。

你似乎知道,XPS文件基本上是一個.zip文件。這意味着雖然基礎數據是XML(即UTF8),但文件本身是壓縮的二進制文件。你不能以任何有意義的方式將它解釋爲文本。

你只要簡單地寫你讀直奔文件的字節數。一個更好版本的代碼應該是這樣的:

private void AcceptXPSData(object client) 
{ 
    string tempFilePath = fileShare + "XPStemp_" + instanceName + ".oxps"; 

    using (TcpClient tcpClient = (TcpClient)client) 
    using (NetworkStream clientStream = tcpClient.GetStream()) 
    using (FileStream fs = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write)) 
    { 
     clientStream.CopyTo(fs); 
    } 

    // processXPS(); 
} 

如果你真的想要,因爲它發生監視I/O,就可以對付它明確,但是更重要的就是比你的代碼是:

private void AcceptXPSData(object client) 
{ 
    string tempFilePath = fileShare + "XPStemp_" + instanceName + ".oxps"; 

    using (TcpClient tcpClient = (TcpClient)client) 
    using (NetworkStream clientStream = tcpClient.GetStream()) 
    using (FileStream fs = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write)) 
    { 
     byte[] message = new byte[65536]; 
     int bytesRead; 

     while ((bytesRead = clientStream.Read(message, 0, message.Length)) > 0) 
     { 
      fs.Write(message, 0, bytesRead); 

      // Add logging or whatever here 
     } 
    } 

    // processXPS(); 
} 

請注意,如果你想處理異常,你只需要處理那些你特別期望可能發生的事情,並且你有一個合理的方法來處理。在這樣的代碼中應該避免使用裸的catch子句或廣泛的catch (Exception)

+0

太好了,謝謝!第一種解決方案正是我所需要的,它完美地工作 - 一旦臨時文件被寫入,我可以擔心任何其他需求。感謝您的快速回復! – 2014-12-11 01:12:23