【问题标题】:C# Wait for all dates from redirectingC#等待重定向的所有日期
【发布时间】:2015-12-14 11:29:06
【问题描述】:

我正在将我的进程 (proc) 的输出重定向到 Richtextbox (richTextBoxOutput)。在重定向之前,它会写入richtextbox 语句“BEFORE OUTPUT DATA”。然后它应该写入来自进程的所有数据并之后写入句子“AFTER OUTPUT DATA”。但是,这最后一句话永远不会结束。它总是在重定向日期之间的富文本框中间的某个地方。你能帮我解决一下吗?

richTextBoxOutput.AppendText("BEFORE OUTPUT DATA");
Process proc = new Process();
proc.StartInfo.FileName = command;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.OutputDataReceived += new DataReceivedEventHandler
(
            (s, e) =>
            {
                if (richTextBoxOutput.InvokeRequired)
                {
                    richTextBoxOutput.Invoke(new Action(() => richTextBoxOutput.AppendText(e.Data + "\n")));
                }
                else
                    richTextBoxOutput.AppendText(e.Data + "\n");
            }
);
proc.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { richTextBoxOutput.AppendText(e.Data + "\n"); });
proc.Start();
proc.BeginOutputReadLine();
while (!proc.HasExited)
{
        Application.DoEvents(); //Instead of proc.WaitForExit()             
}
richTextBoxOutput.AppendText("AFTER OUTPUT DATA");

【问题讨论】:

  • 也可以在 proc 退出后尝试proc.CancelOutputRead();。这将完成所有正在进行的读取操作。
  • 当我使用proc.CancelOutputRead()时,它可以工作,但有时它比输出快,并没有全部显示。
  • 如何检查 OutputDataReceived 是否以输出结尾?
  • 我建议不要使用Application.DoEvents(); 它会给你带来很多麻烦。尝试在主 UI 线程之外的另一个线程中运行整个代码。那么你可以使用proc.WaitForExit或者你甚至可以使用同步读取。

标签: c# io-redirection


【解决方案1】:

最小的工作样本:

    private void btnRun_Click(object sender, EventArgs e) {
        Task.Factory.StartNew(this.StdOutWorker);
    }

    private void StdOutWorker() {
        this.AppendLine("BEFORE OUTPUT DATA");

        // "CmdRandomGenerator.exe" will print random numbers to standard output in infinity loop
        ProcessStartInfo pi = new ProcessStartInfo("CmdRandomGenerator.exe") {
            RedirectStandardOutput = true,
            UseShellExecute = false
        };
        var proc = new Process{
            StartInfo = pi,
            EnableRaisingEvents = true
        };
        proc.Start();

        while (!proc.HasExited) {
            var line = proc.StandardOutput.ReadLine();
            this.AppendLine(line);
        }

        this.AppendLine("AFTER OUTPUT DATA");
    }

    private void AppendLine(string line) {
        Action act = () => {
            this.rtbOutput.AppendText(line + Environment.NewLine);
        };

        // UI objects must be accessed in UI thread
        this.BeginInvoke(act);
    }

这个想法是,从进程中读取是在后台线程中,并且值被传递到 UI 线程而不等待结果(触发并忘记)。

理论上,来自观察过程的值可能会比 UI 处理的更快。在这种情况下,您必须丢弃一些值(采样)。 但是我用while(true) { Console.WriteLine(random()); } 尝试过,UI 速度较慢但仍然响应。

【讨论】:

    【解决方案2】:

    我可以建议一个异步方法来解决这个问题吗?
    是的,您将不得不处理跨线程调用,因为您正在从另一个线程写入 Windows 表单(类似文本框)组件,但好的部分是您可以使用 proc.WaitForExit 而不必担心冻结界面(没有那个讨厌的非常无效的循环,因为它会让您的应用烧毁 CPU)并且有一个 ContinueWith 子句来附加您的“输出数据之后”!

    【讨论】:

    • 但我想保持实时重定向。
    • @Guestík 进程的输出可能比您在右文本框中添加文本要快。所以在某些情况下,你根本无法做到实时。
    • @Guestík TcKs 就在这里...实时一切都是谬论!在这种特定情况下,延迟会非常小......我会说即使你也不会注意到它......我们正在谈论一个额外的线程......
    • cp 命令怎么样,它复制文件并实时显示已经复制的文件(-v 参数)。这次我不想冻结应用程序。顺便说一句,对不起我的英语。
    • @Guestík 不,它不会实时更新...不相信我吗?请记住,您的计算机可能正在运行大约 800 个线程(我的计算机在 120 个进程中运行 1600 个线程),并且您只有 4-12 个内核之间的东西......作为概念证明,您可以覆盖 Control.WndProc (msdn.microsoft.com/en-us/library/… ) 基本表单的方法,并看到数千条消息在您的“实时”更新之间传递......
    猜你喜欢
    • 1970-01-01
    • 2020-08-10
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多