【问题标题】:C# : Redirect console application output : How to flush the output?C#:重定向控制台应用程序输出:如何刷新输出?
【发布时间】:2010-11-05 05:46:53
【问题描述】:

我正在生成外部控制台应用程序并使用异步输出重定向。
as shown in this SO post

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

我想尽快收到 OutputDataReceived 事件。

我有一个简单的重定向应用程序,这里有一些观察:
1. 当我调用一个简单的 'while(true) print("X");'控制台应用程序 (C#) 我立即收到输出事件。 2. 当我调用 3d 派对应用程序时,我试图 从命令行 换行,我看到了逐行输出。
3. 当我从我的准系统包装器(见 1)中调用该 3d 派对应用程序时 - 输出以块的形式出现(大约一页大小)。

该应用程序内部发生了什么?

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

【问题讨论】:

    标签: c# console external-process output-redirect


    【解决方案1】:

    看看这个答案。

    How to send input to the console as if the user is typing?

    这个想法是,当进程启动后抛出任何事件时,您将收到输出接收事件。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        我做了更多的研究并修复了微软的 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;
                /// <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>
        

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

        【讨论】:

        • “一个警告是,现在您将获得多个事件以实现大输出,并且可能会出现中间中断” - 这究竟会是一个问题吗?我刚刚在OutputDataReceived 事件中添加了StringBuilder.Append()
        • 这种方法偶尔会在短程序运行时给我空输出,顺便说一句...有没有办法在处理最终值之前等待异步进程完全赶上?
        • 注意,这不应该也覆盖Exited 事件以等待异步操作吗?
        • 这个扩展类有一个很大的优势:它将\r\n添加到通过OutputDataReceived 事件读取的输出数据块中。这可以对重定向的输出进行正确的行处理。
        • 在搜索了太多的答案后,这里找到了,它起作用了谢谢@dean nort
        猜你喜欢
        • 2015-09-11
        • 2011-06-22
        • 1970-01-01
        • 2011-05-28
        • 1970-01-01
        • 1970-01-01
        • 2011-04-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多