C# - 实时控制台输出重定向

标签 c# redirect console

我正在开发一个 C# 应用程序,我需要启动一个外部控制台 程序来执行一些任务(提取文件)。我需要做的是重定向控制台程序的输出。类似 this one 的代码不起作用,因为它仅在控制台程序中写入新行时引发事件,但我使用的是“更新”控制台窗口中显示的内容,而不写入任何新行。每次更新控制台中的文本时如何引发事件?或者只是每隔 X 秒获取一次控制台程序的输出?提前致谢!

最佳答案

我遇到了一个与您描述的非常相似(可能完全相同)的问题:

  1. 我需要将控制台更新异步传送给我。
  2. 无论是否输入换行符,我都需要检测更新。

我最后做的是这样的:

  1. 开始调用 StandardOutput.BaseStream.BeginRead 的“无限”循环。
  2. BeginRead的回调中,检查EndRead的返回值是否为0;这意味着控制台进程已关闭其输出流(即永远不会再向标准输出写入任何内容)。
  3. 由于 BeginRead 强制您使用恒定长度的缓冲区,因此请检查 EndRead 的返回值是否等于缓冲区大小。这意味着可能有更多的输出等待读取,并且可能希望(甚至有必要)将这些输出全部处理成一个片段。我所做的是保留一个 StringBuilder 并附加到目前为止读取的输出。每当读取输出但其长度小于缓冲区长度时,通知自己(我用一个事件来做)有输出,将 StringBuilder 的内容发送给订阅者,然后清除它。

但是,在我的例子中,我只是将更多内容写入控制台的标准输出。我不确定在您的情况下“更新”输出意味着什么。

更新:我刚刚意识到(不是在解释你在做什么,这是一次很好的学习经历吗?)上面概述的逻辑有一个差一错误:如果输出的长度BeginRead 读取的内容正好 等于缓冲区的长度,然后此逻辑会将输出存储在 StringBuilder 中并在尝试查看时阻塞如果有更多输出要追加。只有当/如果有更多输出可用时,“当前”输出才会作为更大字符串的一部分发送回给您。

显然,要 100% 正确地做到这一点,需要一些方法来防止这种情况(或者更大的缓冲区加上对运气的信心)。

更新 2(代码):

免责声明: 此代码不是生产就绪的。这是我快速拼凑出一个概念验证解决方案以完成需要完成的工作的结果。请不要在您的生产应用程序中使用它。如果这段代码导致可怕的事情发生在你身上,我会假装是别人写的。

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# - 实时控制台输出重定向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4501511/

相关文章:

c# - 枚举参数的 DefaultValue 和 RawDefaultValue 存在意外差异

c# - 一个方法可以实现多个接口(interface)方法吗?

javascript - 如何检测导致页面重定向的 Javascript 代码?

javascript - 将所有 Javascript 控制台输出发送到 DOM 元素

java - 是否可以在控制台中打印Javadoc?或者将 Javadoc 内容导出到字符串?

c# - Json.net 自定义 JsonConvert for Dictionary<string,object>[]

c# - T4 命名空间不能直接包含字段或方法等成员

redirect - jQuery Mobile 301 重定向问题

linux - .htaccess - 将子目录添加到所有目录

javascript - 有没有办法过滤 Google Chrome 控制台中的输出?