Thursday, January 6, 2011

Capturing both output and error streams from a .NET process

MSDN documents a well-known problem that needs to be worked around when reading output from a console process started using System.Diagnostics.Process:

The code example avoids a deadlock condition by calling p.StandardOutput.ReadToEnd before p.WaitForExit. 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. The parent process would wait indefinitely for the child process to exit. The child process would wait indefinitely for the parent to read from the full StandardOutput stream.

The example in MSDN is:

// Start the child process.
 Process p = new Process();
 // Redirect the output stream of the child process.
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = "Write500Lines.exe";
 // Do not wait for the child process to exit before
 // reading to the end of its redirected stream.
 // p.WaitForExit();
 // Read the output stream first and then wait.
 string output = p.StandardOutput.ReadToEnd();

The problem with this example is that it only reads from stdout - not from stderr. One solution, using the new .NET TPL (Task Parallel Library), is to launch two tasks that read the output and error streams.

ProcessStartInfo psInfo = new ProcessStartInfo
    Arguments = @"/c dir C:\",
    FileName = @"C:\Windows\system32\cmd.exe",
    RedirectStandardOutput = true,
    RedirectStandardError = true,
    UseShellExecute = false

Process proc = Process.Start(psInfo);
string stderr = null, stdin = null;
Parallel.Invoke(() => stdin = proc.StandardOutput.ReadToEnd(),
    () => stderr = proc.StandardError.ReadToEnd());

There is one caveat with this approach - Parallel.Invoke does not seem to guarantee that both tasks will run in parallel. I will need to investigate this some more.


Post a Comment