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

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

  1. 制作新流程
  2. 设置startinfo -FileName,Arguments,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(); } 

看起来异步读取流输出有点坏 – 在进程退出之前不会读取所有数据。 即使你调用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 ... } 

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

很少有东西妨碍它…控制台应用程序可能使用“\ 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; } } 

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