2014-12-10 7 views
0

Я пытаюсь создать приложение «виртуального принтера» на C#, которое получает задания печати по сети, анализирует необработанные данные печати для определенной информации и затем сохраняет документ в базу данных. Измененная версия следующего класса работает для заданий постскриптума печати (он сохраняет входящие данные в действительный .prn-файл, точно так же, как если бы принтер был настроен для печати на порт «FILE:».) Когда я пытаюсь выполнить захват. Документы XPS от Microsoft XPS Document Writer, однако, документы не могут быть открыты. Допустимые файлы XPS также должны быть действительными ZIP-файлами, если расширение переименовано, и это тоже не работает. Когда я печатаю тот же документ в порт FILE: а затем в свое приложение, и я сравниваю результаты в Notepad ++, существует 5-символьная разница в длине данных, но она выглядит одинаково (это не простой текст, поэтому трудно смотреть, но первые несколько символов и последние несколько символов выглядят одинаково). Файл с сохраненным «нормальным» способом работает нормально, но файл, созданный моим кодом, не работает.Файлы XPS, полученные через TcpClient, повреждены

В общем, я пытаюсь получить произвольные данные через порт TCP и записать его в файл. Мое решение «близко», но не работает. Я не знаю, что такое кодирование XPS, но я использую ASCII для постскриптума, и я попробовал 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 - Я использую драйвер печати Microsoft XPS Document Writer, просто пытаясь использовать другой порт для отправки результатов в мое приложение, а не запроса сохранения пользователя на жесткий диск. Причиной этого является автоматизация способа имени и хранения файлов. Это будет кошмар, чтобы поддерживать, если файлы теряются каждый раз, когда пользователь называет его неправильно. –

+0

Ну, вы можете использовать двоичный редактор в Visual Studio, чтобы узнать, что такое _precise_ разница между файлами. Я до сих пор не понимаю, как здесь задействован TcpClient, но это не искажает ваши данные, я могу сказать вам многое. Вы не используете его правильно - нет оснований полагать, что ценность данных в целом файла обязательно будет получена в одном вызове 'Read()'. Re encoding: если вы просто записываете данные в файл, не интерпретируйте их вообще ... просто напишите байты прямо в файл. XML (и поэтому XPS) использует UTF8, но, похоже, это не имеет значения. –

+0

Я не обвиняю TCPClient, у меня просто ошибка. Read() находится в цикле - он заполняет буфер, а затем добавляет его в файл, пока не будет больше данных. Отладка будет получена из кода «Bytes Read: 65536 Bytes Read: 65536 Bytes Read: 45888 Bytes Read: 0 "- буфер заполняется дважды, а затем считывает оставшиеся 45888 байт. Это составляет 172,8 kb, что является точным размером файла, который я получаю, когда я «печатаю файл в обычном режиме», но, что интересно, файл, который генерирует моя программа, составляет 310kb, а не 173 kb, как указывает отладка. Двоичный редактор показывает «похожие данные», но много лишнего мусора в моем. –

ответ

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(); 
} 

Если вы действительно хотите, чтобы контролировать ввода/вывода, как это происходит, вы можете иметь дело с этим явно, но все же гораздо проще, чем ваш код был:

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(); 
} 

Обратите внимание, что если вы хотите обрабатывать исключения, вам необходимо обрабатывать только те, которые вы ожидаете, может произойти, и для которых у вас есть разумный способ справиться. Bare catch следует избегать статей или catch (Exception).

+0

Отлично, спасибо! Первое решение - именно то, что мне нужно, и оно отлично работает - я могу беспокоиться о любых других требованиях после создания файла temp. Спасибо за быстрые ответы! –

Смежные вопросы