C#:重定向控制台应用程序输出:如何刷新输出?

我正在产生外部控制台应用程序并使用异步输出重定向。
如此SOpost所示

我的问题是,在得到OutputDataReceived事件通知之前,生成的进程似乎需要产生一定量的输出。

我想尽快收到OutputDataReceived事件。

我有一个简单的重定向应用程序,这里有一些观察:
1.当我打电话给简单的’while(true)打印(“X”)时; 控制台应用程序(C#)我立即收到输出事件。 2.当我调用3d派对应用程序时,我试图从命令行行,我看到逐行输出。
3.当我从裸骨包装器中调用该3d派对应用程序时(参见1) – 输出以块(约一页大小)的forms出现。

该应用内发生了什么?

仅供参考:有问题的应用程序是“USBee DX数据Exctarctor(异步总线)v1.0”。

我做了一些更多的研究并修复了microsofts Process类。 但由于我的最后一个答案被删除而没有任何理由,我不得不创建一个新答案。

所以拿这个例子……

创建一个Windows应用程序并在主窗体上粘贴一个富文本框,然后将其添加到窗体加载…

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结束,它假设有更多的comming并将其附加到下一个事件的开始。

为了解决这个问题,我已经为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; /// Gets the line of characters that was written to a redirected  output stream. /// The line that was written by an associated  to its redirected  or  stream. /// 2 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> 

无需对现有代码进行任何其他更改。 它仍然是异步并且很好地包裹起来。 需要注意的是,现在您将获得大量输出的多个事件,并且中间存在潜在的中断,因此您需要自己处理这种情况。 除此之外,它应该是好的。

看看这个答案。

如何将输入发送到控制台,就像用户正在键入一样?

我们的想法是,在进程启动后抛出任何事件时,您将收到输出的接收事件。

似乎问题在于虚拟应用程序是用c#编写的,每个println自动刷新输出,而第三方应用程序是用c / c ++编写的,因此只在stdoutbuffer满时写入。 我发现的唯一解决方案是确保c / c ++应用程序在每次打印后刷新或将其缓冲区设置为0。