I have problem with reading the output of one Process asynchronously in C#. I found some other similar questions on this site but they don\'t really help me. Here is what I
It seems that reading stream output asynchronously is a bit broken - not all the data is read before the process exits. Even if you call Process.WaitForExit()
and even if you then call Process.Close()
(or Dispose()
) you can still get a lot of data afterwards. See http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/ for a full write-up, but the solution is basically to use synchronous methods. To avoid a deadlock, though, you have to call one of them on another thread:
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 ...
}
There are few things that are getting in the way of it...
The console app is probably using "\b" backspace to overwrite the percentage, its maybe not flushing to the stdout
stream after every write, and the BeginOutputReadLine
presumably waits for the end of line before giving you data.
See how you get on with reading process.StandardOutput.BaseStream
via BeginRead
(this code isn't proper async and the "\b"s will need processed differently if your putting progress in a form):
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.WaitForExit()
will wait until the asynchronous output / error stream reading finished. Unfortunately this is not true for Process.WaitForExit(timeout)
overload. This is what the Process
class does internally:
//...
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);
}
... So it will wait for the async reads only if there was no timeout!
To fix it simply call the parameterless WaitForExit()
after WaitForExit(timeout)
returned true:
// ...
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");
}
For details read the remarks here: http://msdn.microsoft.com/en-us/library/ty0d8k56%28v=vs.110%29
What about using a StreamReader
on process.StandardOutput
, and the using the Read()
method ?
http://msdn.microsoft.com/fr-fr/library/system.io.streamreader.read(v=vs.80).aspx