【问题标题】:How to read to end process output asynchronously in C#?如何在 C# 中异步读取以结束进程输出?
【发布时间】:2012-03-20 22:37:45
【问题描述】:

我在 C# 中异步读取一个进程的输出时遇到问题。 我在这个网站上发现了一些其他类似的问题,但它们并没有真正帮助我。 这是我的工作:

  1. 新建流程
  2. 设置启动信息 -FileName、参数、CreateNoWindow(true)、UseShellExecute(false)、RedirectStandardOutput(true)
  3. 将事件处理程序添加到 OutputDataReceived;
  4. 启动进程,BeginOutputReadLine 然后 WaitForExit()。

它工作正常,但启动进程的输出写入了一些我想得到的百分比(%),但我不能,因为我的代码逐行读取并且百分比不显示。

例子:

%0,%1...%100
Finished.

我的输出:

%0
Finished. 

这是我程序的当前代码:

StringBuilder sBuilder = new StringBuilder();
static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    sBuilder.AppendLine(e.Data);
}

static void CommandExecutor()
{
    Process process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = /*path of the program*/,
            Arguments = /*arguments*/,
            CreateNoWindow = true,
            UseShellExecute = false,
            WindowStyle = ProcessWindowStyle.Hidden,
            RedirectStandardOutput = true
        }
    };

    process.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);

    process.Start();

    process.BeginOutputReadLine();

    process.WaitForExit();
}

【问题讨论】:

  • 你有没有在appenline方法上下断点,看看它被命中了多少次?在控制台应用程序中,百分比是相互覆盖还是连续的?
  • 百分比覆盖。我会放一个断点看看会发生什么...
  • 它不是偶然称为 BeginOutputReadLine 的。该程序不输出任何行。除非程序显式调用flush(),否则在重定向输出时它通常不会输出任何内容。你无法解决这个问题。

标签: c# asynchronous process


【解决方案1】:

Process.WaitForExit() 将等待异步输出/错误流读取完成。不幸的是,Process.WaitForExit(timeout) 过载并非如此。这就是 Process 类在内部所做的:

//...

finally
{
    if (processWaitHandle != null)
    {
        processWaitHandle.Close();
    }
    if (this.output != null && milliseconds == -1)
    {
        this.output.WaitUtilEOF();
    }
    if (this.error != null && milliseconds == -1)
    {
        this.error.WaitUtilEOF();
    }
    this.ReleaseProcessHandle(safeProcessHandle);
}

...所以它只会在没有超时的情况下等待异步读取! 要修复它,只需在 WaitForExit(timeout) 返回 true 后调用 parameterless WaitForExit()

// ...

if (process.WaitForExit( 10 * 1000 ) && process.WaitForExit() )
{
 // Process'es OutputDataReceived / ErrorDataReceived callbacks will not be called again, EOF streams reached
}
else
{
   throw new Exception("timeout");
}

详情请看备注:http://msdn.microsoft.com/en-us/library/ty0d8k56%28v=vs.110%29

【讨论】:

  • 这是解决此问题的正确方法。应该是官方答案。
  • 如果该过程未完成,这将不起作用。例如,如果你想隔离进程中的代码,因为它可以进入僵尸状态,这种方法将永远停留在 WaitForExit() 语句上:-(
【解决方案2】:

似乎异步读取流输出有点坏 - 在进程退出之前并非所有数据都被读取。即使您致电Process.WaitForExit(),即使您随后致电Process.Close()(或Dispose()),之后您仍然可以获得大量数据。有关完整的文章,请参阅http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/,但解决方案基本上是使用同步方法。但是,为了避免死锁,您必须在另一个线程上调用其中一个:

using (var process = Process.Start(processInfo))
{
    // Read stderr synchronously (on another thread)

    string errorText = null;
    var stderrThread = new Thread(() => { errorText = process.StandardError.ReadToEnd(); });
    stderrThread.Start();

    // Read stdout synchronously (on this thread)

    while (true)
    {
        var line = process.StandardOutput.ReadLine();
        if (line == null)
            break;

        // ... Do something with the line here ...
    }

    process.WaitForExit();
    stderrThread.Join();

    // ... Here you can do something with errorText ...
}

【讨论】:

  • 谢谢,为我工作。傻坏了 BeginOutputReadLine
  • @EM0,链接好像坏了。
  • 不是正确的解决方案。检查@Sly 的答案。
  • Sly 的回答肯定会带来一个很大的“陷阱”,但问题和我的回答都没有使用WaitForExit(timeout),所以我不确定为什么这不是“正确的解决方案”原来的问题。你能详细说明@Deathicon 有什么问题吗?
【解决方案3】:

有一些事情会妨碍它......

控制台应用程序可能正在使用“\b”退格来覆盖百分比,它可能不会在每次写入后刷新到stdout 流,并且BeginOutputReadLine 可能会在给您数据之前等待行尾。

通过BeginRead 了解您如何继续阅读process.StandardOutput.BaseStream(此代码不正确async,如果您将进度放入表单,则“\b”将需要以不同方式处理):

        while (true)
        {
            byte[] buffer = new byte[256];
            var ar = myProcess.StandardOutput.BaseStream.BeginRead(buffer, 0, 256, null, null);
            ar.AsyncWaitHandle.WaitOne();
            var bytesRead = myProcess.StandardOutput.BaseStream.EndRead(ar);
            if (bytesRead > 0)
            {
                Console.Write(Encoding.ASCII.GetString(buffer, 0, bytesRead));
            }
            else
            {
                myProcess.WaitForExit();
                break;
            }
        }

【讨论】:

    【解决方案4】:

    process.StandardOutput 上使用StreamReader 和使用Read() 方法怎么样? http://msdn.microsoft.com/fr-fr/library/system.io.streamreader.read(v=vs.80).aspx

    【讨论】:

    • 我已经尝试过了,但它会在进程完成时显示输出,我无法获得百分比...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-25
    • 1970-01-01
    • 2016-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-20
    相关资源
    最近更新 更多