2009-06-23 3 views
5

Я создаю внешнее консольное приложение и перенаправляю выход асинхронного вывода.
as shown in this SO postC#: Перенаправить вывод приложения консоли: как очистить вывод?

Моя проблема в том, что, кажется, что порождал процесс должен производить определенное количество продукции, прежде чем я получить уведомление о событии в OutputDataReceived.

Я хочу получить событие OutputDataReceived как можно скорее.

У меня есть приложение для переадресации голосовой кости, и вот несколько замечаний:
1. Когда я называю простой «пока» («истинный») («X»); консольное приложение (C#) Я получаю выходное событие немедленно. 2. Когда я вызываю приложение 3d-вечеринки, я пытаюсь обернуть из командной строки Я вижу линейный вывод.
3. Когда я вызываю это приложение для 3d-вечеринки из моей обложки с голыми костями (см. 1) - вывод идет в кусках (примерно один размер страницы).

Что происходит внутри этого приложения?

FYI: Данное приложение представляет собой «Экзактор данных USBEE DX (шина Async) v1.0».

ответ

8

Я провел еще несколько исследований и исправил микропроцессы класса Process. Но поскольку мой последний ответ был удален без причины, мне пришлось создать новый.

Так возьмите этот пример ...

Создание окна приложения и наклеить богатое текстовое поле на главной форме, затем добавьте в виде нагрузки ...

 Process p = new Process() 
     { 
      StartInfo = new ProcessStartInfo() 
      { 
       FileName = "cmd.exe", 
       CreateNoWindow = true, 
       UseShellExecute = false, 
       ErrorDialog = false, 
       RedirectStandardInput = true, 
       RedirectStandardOutput = true, 
       RedirectStandardError = true, 
      }, 
      EnableRaisingEvents = true, 
      SynchronizingObject = this 
     }; 

     p.OutputDataReceived += (s, ea) => this.richTextBox1.AppendText(ea.Data); 

     p.Start(); 
     p.BeginOutputReadLine(); 

Этот выход будет что-то как это ...

Microsoft Windows [Version 6.1.7601] 
Copyright (c) 2009 Microsoft Corporation. All rights reserved. 

Событие OutputDataReceived не запускается для последней строки. После некоторого ILSpying кажется, что это преднамеренно, потому что последняя строка не заканчивается crlf, она предполагает, что есть больше сообщений и добавляет их к началу следующего события.

Чтобы исправить это, я написал оболочку для класса Process и взял с собой некоторые из необходимых внутренних классов, чтобы все работало аккуратно. Вот класс FixedProcess ...

using System; 
using System.Collections; 
using System.IO; 
using System.Text; 
using System.Threading; 

namespace System.Diagnostics 
{ 
    internal delegate void UserCallBack(string data); 
    public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e); 

    public class FixedProcess : Process 
    { 
     internal AsyncStreamReader output; 
     internal AsyncStreamReader error; 
     public event DataReceivedEventHandler OutputDataReceived; 
     public event DataReceivedEventHandler ErrorDataReceived; 

     public new void BeginOutputReadLine() 
     { 
      Stream baseStream = StandardOutput.BaseStream; 
      this.output = new AsyncStreamReader(this, baseStream, new UserCallBack(this.FixedOutputReadNotifyUser), StandardOutput.CurrentEncoding); 
      this.output.BeginReadLine(); 
     } 

     public void BeginErrorReadLine() 
     { 
      Stream baseStream = StandardError.BaseStream; 
      this.error = new AsyncStreamReader(this, baseStream, new UserCallBack(this.FixedErrorReadNotifyUser), StandardError.CurrentEncoding); 
      this.error.BeginReadLine(); 
     } 

     internal void FixedOutputReadNotifyUser(string data) 
     { 
      DataReceivedEventHandler outputDataReceived = this.OutputDataReceived; 
      if (outputDataReceived != null) 
      { 
       DataReceivedEventArgs dataReceivedEventArgs = new DataReceivedEventArgs(data); 
       if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) 
       { 
        this.SynchronizingObject.Invoke(outputDataReceived, new object[] 
        { 
         this, 
         dataReceivedEventArgs 
        }); 
        return; 
       } 
       outputDataReceived(this, dataReceivedEventArgs); 
      } 
     } 

     internal void FixedErrorReadNotifyUser(string data) 
     { 
      DataReceivedEventHandler errorDataReceived = this.ErrorDataReceived; 
      if (errorDataReceived != null) 
      { 
       DataReceivedEventArgs dataReceivedEventArgs = new DataReceivedEventArgs(data); 
       if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) 
       { 
        this.SynchronizingObject.Invoke(errorDataReceived, new object[] 
        { 
         this, 
         dataReceivedEventArgs 
        }); 
        return; 
       } 
       errorDataReceived(this, dataReceivedEventArgs); 
      } 
     } 
    } 

    internal class AsyncStreamReader : IDisposable 
    { 
     internal const int DefaultBufferSize = 1024; 
     private const int MinBufferSize = 128; 
     private Stream stream; 
     private Encoding encoding; 
     private Decoder decoder; 
     private byte[] byteBuffer; 
     private char[] charBuffer; 
     private int _maxCharsPerBuffer; 
     private Process process; 
     private UserCallBack userCallBack; 
     private bool cancelOperation; 
     private ManualResetEvent eofEvent; 
     private Queue messageQueue; 
     private StringBuilder sb; 
     private bool bLastCarriageReturn; 
     public virtual Encoding CurrentEncoding 
     { 
      get 
      { 
       return this.encoding; 
      } 
     } 
     public virtual Stream BaseStream 
     { 
      get 
      { 
       return this.stream; 
      } 
     } 
     internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding) 
      : this(process, stream, callback, encoding, 1024) 
     { 
     } 
     internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize) 
     { 
      this.Init(process, stream, callback, encoding, bufferSize); 
      this.messageQueue = new Queue(); 
     } 
     private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize) 
     { 
      this.process = process; 
      this.stream = stream; 
      this.encoding = encoding; 
      this.userCallBack = callback; 
      this.decoder = encoding.GetDecoder(); 
      if (bufferSize < 128) 
      { 
       bufferSize = 128; 
      } 
      this.byteBuffer = new byte[bufferSize]; 
      this._maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize); 
      this.charBuffer = new char[this._maxCharsPerBuffer]; 
      this.cancelOperation = false; 
      this.eofEvent = new ManualResetEvent(false); 
      this.sb = null; 
      this.bLastCarriageReturn = false; 
     } 
     public virtual void Close() 
     { 
      this.Dispose(true); 
     } 
     void IDisposable.Dispose() 
     { 
      this.Dispose(true); 
      GC.SuppressFinalize(this); 
     } 
     protected virtual void Dispose(bool disposing) 
     { 
      if (disposing && this.stream != null) 
      { 
       this.stream.Close(); 
      } 
      if (this.stream != null) 
      { 
       this.stream = null; 
       this.encoding = null; 
       this.decoder = null; 
       this.byteBuffer = null; 
       this.charBuffer = null; 
      } 
      if (this.eofEvent != null) 
      { 
       this.eofEvent.Close(); 
       this.eofEvent = null; 
      } 
     } 
     internal void BeginReadLine() 
     { 
      if (this.cancelOperation) 
      { 
       this.cancelOperation = false; 
      } 
      if (this.sb == null) 
      { 
       this.sb = new StringBuilder(1024); 
       this.stream.BeginRead(this.byteBuffer, 0, this.byteBuffer.Length, new AsyncCallback(this.ReadBuffer), null); 
       return; 
      } 
      this.FlushMessageQueue(); 
     } 
     internal void CancelOperation() 
     { 
      this.cancelOperation = true; 
     } 
     private void ReadBuffer(IAsyncResult ar) 
     { 
      int num; 
      try 
      { 
       num = this.stream.EndRead(ar); 
      } 
      catch (IOException) 
      { 
       num = 0; 
      } 
      catch (OperationCanceledException) 
      { 
       num = 0; 
      } 
      if (num == 0) 
      { 
       lock (this.messageQueue) 
       { 
        if (this.sb.Length != 0) 
        { 
         this.messageQueue.Enqueue(this.sb.ToString()); 
         this.sb.Length = 0; 
        } 
        this.messageQueue.Enqueue(null); 
       } 
       try 
       { 
        this.FlushMessageQueue(); 
        return; 
       } 
       finally 
       { 
        this.eofEvent.Set(); 
       } 
      } 
      int chars = this.decoder.GetChars(this.byteBuffer, 0, num, this.charBuffer, 0); 
      this.sb.Append(this.charBuffer, 0, chars); 
      this.GetLinesFromStringBuilder(); 
      this.stream.BeginRead(this.byteBuffer, 0, this.byteBuffer.Length, new AsyncCallback(this.ReadBuffer), null); 
     } 
     private void GetLinesFromStringBuilder() 
     { 
      int i = 0; 
      int num = 0; 
      int length = this.sb.Length; 
      if (this.bLastCarriageReturn && length > 0 && this.sb[0] == '\n') 
      { 
       i = 1; 
       num = 1; 
       this.bLastCarriageReturn = false; 
      } 
      while (i < length) 
     { 
      char c = this.sb[i]; 
      if (c == '\r' || c == '\n') 
      { 
       if (c == '\r' && i + 1 < length && this.sb[i + 1] == '\n') 
       { 
        i++; 
       } 

       string obj = this.sb.ToString(num, i + 1 - num); 

       num = i + 1; 

       lock (this.messageQueue) 
       { 
        this.messageQueue.Enqueue(obj); 
       } 
      } 
      i++; 
     } 

      // Flush Fix: Send Whatever is left in the buffer 
      string endOfBuffer = this.sb.ToString(num, length - num); 
      lock (this.messageQueue) 
      { 
       this.messageQueue.Enqueue(endOfBuffer); 
       num = length; 
      } 
      // End Flush Fix 

      if (this.sb[length - 1] == '\r') 
      { 
       this.bLastCarriageReturn = true; 
      } 
      if (num < length) 
      { 
       this.sb.Remove(0, num); 
      } 
      else 
      { 
       this.sb.Length = 0; 
      } 
      this.FlushMessageQueue(); 
     } 
     private void FlushMessageQueue() 
     { 
      while (this.messageQueue.Count > 0) 
      { 
       lock (this.messageQueue) 
       { 
        if (this.messageQueue.Count > 0) 
        { 
         string data = (string)this.messageQueue.Dequeue(); 
         if (!this.cancelOperation) 
         { 
          this.userCallBack(data); 
         } 
        } 
        continue; 
       } 
       break; 
      } 
     } 
     internal void WaitUtilEOF() 
     { 
      if (this.eofEvent != null) 
      { 
       this.eofEvent.WaitOne(); 
       this.eofEvent.Close(); 
       this.eofEvent = null; 
      } 
     } 
    } 

    public class DataReceivedEventArgs : EventArgs 
    { 
     internal string _data; 
     /// <summary>Gets the line of characters that was written to a redirected <see cref="T:System.Diagnostics.Process" /> output stream.</summary> 
     /// <returns>The line that was written by an associated <see cref="T:System.Diagnostics.Process" /> to its redirected <see cref="P:System.Diagnostics.Process.StandardOutput" /> or <see cref="P:System.Diagnostics.Process.StandardError" /> stream.</returns> 
     /// <filterpriority>2</filterpriority> 
     public string Data 
     { 
      get 
      { 
       return this._data; 
      } 
     } 
     internal DataReceivedEventArgs(string data) 
     { 
      this._data = data; 
     } 
    } 
} 

Придерживайтесь того, что в вашем проекте, а затем измените ...

Process p = new Process() 
{ 
    .... 

в

FixedProcess p = new FixedProcess() 
{ 
    .... 

Теперь приложение должно показать что-то вроде этого ...

Microsoft Windows [Version 6.1.7601] 

Copyright (c) 2009 Microsoft Corporation. All rights reserved. 

C:\Projects\FixedProcess\bin\Debug> 

без необходимости внесения каких-либо других изменений в существующий код. Он также по-прежнему асинхронно и хорошо завербован. Одно из предостережений заключается в том, что теперь вы получите несколько событий для большого вывода с потенциальными перерывами между ними, поэтому вам придется самому справиться с этим сценарием. Кроме этого, все должно быть хорошо.

+0

«Единственное предостережение в том, что теперь вы получите несколько событий для большого вывода с потенциальными перерывами между ними». Как это было бы проблемой? Я просто добавил 'StringBuilder.Append()' к событию 'OutputDataReceived'. – Nyerguds

+0

Этот метод иногда дает мне пустой вывод на коротких прогонах программ, btw ... есть способ дождаться, когда процесс async полностью догонит до обработки конечного значения? – Nyerguds

+0

В этой заметке, не следует ли также переопределять событие «Выход» для ожидания операций async? – Nyerguds

1

Похоже, проблема заключалась в том, что фиктивное приложение было написано в C#, которое автоматически удаляет вывод каждый раз при печати, в то время как стороннее приложение было написано в c/C++ и поэтому было написано только при заполнении stdoutbuffer. Единственное решение, которое было найдено, заключается в том, чтобы убедиться, что приложение c/C++ сбрасывается после каждой печати или для установки его буфера на 0.

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