Correct way to handle standard error and output from a program when spawned via Process class from C#?
Asked Answered
E

2

7

I read the documentation for Process.StandardOutput, which has this quote:

A deadlock condition can result if the parent process calls p.WaitForExit before p.StandardOutput.ReadToEnd and the child process writes enough text to fill the redirected stream.

So I'm wondering. What is the correct way to do this if I'm also afraid that StandardError could be filled in some scenarios?

Do I have to use a loop to alternate reading from standard output and error, to avoid either filling up, or is this simple code enough:

string error = proc.StandardError.ReadToEnd();
string output = proc.StandardOutput.ReadToEnd();
bool didFinish = proc.WaitForExit(60000);

Edited after some answers have been posted

So this is the right approach?

var output = new StringBuilder();
proc.OutputDataReceived += (s, e) => output.Append(e.Data);
proc.BeginOutputReadLine();
string error = proc.StandardError.ReadToEnd();
bool didFinish = proc.WaitForExit(60000);

And then I use the stringbuilder content only if the process actually finished.

Is that the right approach then?

Extramundane answered 9/11, 2010 at 18:54 Comment(1)
H
7

Your example code could cause a deadlock situtation where there was something written to the StandardOutput and not to StandardError. The very next example from the documentation you linked states as much.

Essentially, what I would recommend, is using the async reads on both streams to fill a buffer as the Streams are written to, and then call WaitForExit.

Hetaera answered 9/11, 2010 at 19:3 Comment(3)
Yeah I'm curious why you don't just use this method as given in the MSDN You can use asynchronous read operations to avoid these dependencies and their deadlock potential. Alternately, you can avoid the deadlock condition by creating two threads and reading the output of each stream on a separate thread.Hustle
Edited in code using BeginOutputReadLine and OutputDataReceived, can you take a look and see if the posted code is correct?Extramundane
This is what I ended up with: bitbucket.org/lassevk/mercurial.net/src/728f85d67447/…Extramundane
A
3

The problem arises because the child process writes its standard output and standard error into a pair of pipes, which are given a finite buffer by the OS. If the parent is not actively reading both of them then they're liable to fill up. When a pipe fills up, any subsequent writes to it are blocked.

In your example you're reading all of StandardError then all of StandardOutput. This works OK if the child process only writes a little bit of data to StandardError and/or StandardOutput. It's a problem if the child process wants to write a lot of data to StandardOutput. While the parent process is waiting to consume data from StandardError, the child process is busy filling up the StandardOutput buffer.

The safest way is to read from standard in and standard error concurrently. There's a few ways to do this:

  • Spawn separate threads and call ReadToEnd on each
  • Use BeginRead and EndRead on one thread
  • Add handlers on the Process.ErrorDataReceived and Process.OutputDataReceived events, then call Process.BeginErrorReadLine and Process.BeginOutputReadLine.
Antifreeze answered 9/11, 2010 at 19:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.