C# - 实时控制台输出重定向 [英] C# - Realtime console output redirection

查看:891
本文介绍了C# - 实时控制台输出重定向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个C#应用程序,我需要启动一个外部的控制台的程序来执行某些任务(提取文件)。我需要做的就是将控制台重定向程序的输出。 code像<一个href=\"http://stackoverflow.com/questions/415620/redirect-console-output-to-textbox-in-separate-program-c\">this 之一是不行的,因为它引发的事件只有当一个新的生产线在控制台程序writen,但我使用的一个更新什么是在控制台窗口中,没有任何书面方式新线路。我怎样才能在每一个控制台中的文字更新时引发一个事件?或者只是让每一个X秒控制台程序的输出?在此先感谢!

I'm developing a C# application and I need to start an external console program to perform some tasks (extract files). What I need to do is to redirect the output of the console program. Code like this one does not work, because it raises events only when a new line is writen in the console program, but the one I use "updates" what's shown in the console window, without writting any new lines. How can I raise an event every time the text in the console is updated? Or just get the output of the console program every X seconds? Thanks in advance!

推荐答案

我有一个非常类似的(可能是准确的),你描述的问题:

I have had a very similar (possibly the exact) problem as you describe:


  1. 我需要在控制台更新以异步方式交给我。

  2. 我需要不管换行是否是输入要被检测的更新。

我落得这样做是这样的:

What I ended up doing goes like this:


  1. 开始打电话的没完没了循环 StandardOutput.BaseStream.BeginRead

  2. 在回调的BeginRead ,检查 EndRead 的返回值是 0 ;这意味着控制台进程已关闭其输出流(即永远不会再写入任何标准输出)。

  3. 由于的BeginRead 强迫你使用一个定长的缓冲区,检查是否有 EndRead 的返回值等于到缓冲区的大小。这意味着有可能更输出等待读,并可能需要(或者甚至是必要的),该输出在一块的所有处理。我所做的就是保持一个的StringBuilder 周围并追加至今读取输出。每当输出被读出,但它的长度为&lt;缓冲区长度,通知自己(我有一个事件做),有输出,在的StringBuilder 的内容发送给用户,然后将其清除。

  1. Start an "endless" loop of calling StandardOutput.BaseStream.BeginRead.
  2. In the callback for BeginRead, check if the return value of EndRead is 0; this means that the console process has closed its output stream (i.e. will never write anything to standard output again).
  3. Since BeginRead forces you to use a constant-length buffer, check if the return value of EndRead is equal to the buffer size. This means that there may be more output waiting to be read, and it may be desirable (or even necessary) that this output is processed all in one piece. What I did was keep a StringBuilder around and append the output read so far. Whenever output is read but its length is < the buffer length, notify yourself (I do it with an event) that there is output, send the contents of the StringBuilder to the subscriber, and then clear it.

然而后,在我的情况,我简单地写更多的东西到控制台的标准输出。我不知道什么更新输出你的这种情况。

However, in my case I was simply writing more stuff to the console's standard output. I 'm not sure what "updating" the output means in your case.

更新:我才意识到(不解释,你在做什么一个很好的学习经验吗?)是上述逻辑有一种关闭情况​​的一个错误:如果输出的长度通过的BeginRead 读的是的究竟的等于你的缓冲区的长度,那么这个逻辑将存储在的StringBuilder <输出/ code>和块,同时想看看是否有更多的产出要追加。 当前输出将只发送回给你当/如果有更多的输出可作为较大字符串的一部分。

Update: I just realized (isn't explaining what you are doing a great learning experience?) that the logic outlined above has an off-by-one bug: If the length of the output read by BeginRead is exactly equal to the length of your buffer, then this logic will store the output in the StringBuilder and block while trying to see if there's more output to append. The "current" output will only be sent back to you when/if more output is available, as part of a larger string.

显然需要防范这一些方法(或你的运气的一个权力较大的缓冲液和信仰)正确地做到这一点100%。

Obviously some method of guarding against this (or a biggish buffer plus faith in your powers of luck) is needed to do this 100% correctly.

更新2(code):

免责声明:
这code不是生产就绪。这是我很快地一起黑客概念的解决方案证明做什么需要做的结果。它矗立在生产应用程序,请不要使用它。如果这个code使可怕的​​事情发生在你身上,我会pretend别人写的。

public class ConsoleInputReadEventArgs : EventArgs
{
    public ConsoleInputReadEventArgs(string input)
    {
        this.Input = input;
    }

    public string Input { get; private set; }
}

public interface IConsoleAutomator
{
    StreamWriter StandardInput { get; }

    event EventHandler<ConsoleInputReadEventArgs> StandardInputRead;
}

public abstract class ConsoleAutomatorBase : IConsoleAutomator
{
    protected readonly StringBuilder inputAccumulator = new StringBuilder();

    protected readonly byte[] buffer = new byte[256];

    protected volatile bool stopAutomation;

    public StreamWriter StandardInput { get; protected set; }

    protected StreamReader StandardOutput { get; set; }

    protected StreamReader StandardError { get; set; }

    public event EventHandler<ConsoleInputReadEventArgs> StandardInputRead;

    protected void BeginReadAsync()
    {
        if (!this.stopAutomation) {
            this.StandardOutput.BaseStream.BeginRead(this.buffer, 0, this.buffer.Length, this.ReadHappened, null);
        }
    }

    protected virtual void OnAutomationStopped()
    {
        this.stopAutomation = true;
        this.StandardOutput.DiscardBufferedData();
    }

    private void ReadHappened(IAsyncResult asyncResult)
    {
        var bytesRead = this.StandardOutput.BaseStream.EndRead(asyncResult);
        if (bytesRead == 0) {
            this.OnAutomationStopped();
            return;
        }

        var input = this.StandardOutput.CurrentEncoding.GetString(this.buffer, 0, bytesRead);
        this.inputAccumulator.Append(input);

        if (bytesRead < this.buffer.Length) {
            this.OnInputRead(this.inputAccumulator.ToString());
        }

        this.BeginReadAsync();
    }

    private void OnInputRead(string input)
    {
        var handler = this.StandardInputRead;
        if (handler == null) {
            return;
        }

        handler(this, new ConsoleInputReadEventArgs(input));
        this.inputAccumulator.Clear();
    }
}

public class ConsoleAutomator : ConsoleAutomatorBase, IConsoleAutomator
{
    public ConsoleAutomator(StreamWriter standardInput, StreamReader standardOutput)
    {
        this.StandardInput = standardInput;
        this.StandardOutput = standardOutput;
    }

    public void StartAutomate()
    {
        this.stopAutomation = false;
        this.BeginReadAsync();
    }

    public void StopAutomation()
    {
        this.OnAutomationStopped();
    }
}

用于像这样:

var processStartInfo = new ProcessStartInfo
    {
        FileName = "myprocess.exe",
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
    };

var process = Process.Start(processStartInfo);
var automator = new ConsoleAutomator(process.StandardInput, process.StandardOutput);

// AutomatorStandardInputRead is your event handler
automator.StandardInputRead += AutomatorStandardInputRead;
automator.StartAutomate();

// do whatever you want while that process is running
process.WaitForExit();
automator.StandardInputRead -= AutomatorStandardInputRead;
process.Close();

这篇关于C# - 实时控制台输出重定向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆