如何在C#中异步读取结束进程输出?

时间:2012-03-02 12:12:06

标签: c# asynchronous process

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

  1. 制作新流程
  2. 设置startinfo -FileName,Arguments,CreateNoWindow(true),UseShellExecute(false),RedirectStandardOutput(true)
  3. 将事件处理程序添加到OutputDataReceived;
  4. 启动进程,BeginOutputReadLine,然后是WaitForExit()。
  5. 它工作正常,但启动过程的输出写了一些我想要得到的百分比(%),但我不能,因为我的代码逐行读取并且百分比没有出现。 / p>

    示例:

    %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();
    }
    

4 个答案:

答案 0 :(得分:7)

似乎异步读取流输出有点坏 - 在进程退出之前不会读取所有数据。即使您拨打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 ...
}

答案 1 :(得分:5)

Process.WaitForExit()将一直等到异步输出/错误流读取完成。不幸的是,对于Process.WaitForExit(超时)重载,情况并非如此。这就是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后调用无参数的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

答案 2 :(得分:1)

很少有事情妨碍它...... 控制台应用程序可能使用“\ b”退格键来覆盖百分比,它可能在每次写入后都不会刷新到stdout流,而BeginOutputReadLine可能会在给出数据之前等待行尾。

通过BeginRead了解如何继续阅读process.StandardOutput.BaseStream(此代码不是正确的异步,如果您在表单中的推送进度,则需要处理“\ 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;
            }
        }

答案 3 :(得分:0)

如何在process.StandardOutput上使用StreamReader,并使用Read()方法? http://msdn.microsoft.com/fr-fr/library/system.io.streamreader.read(v=vs.80).aspx