Often one would like to read both StdErr and StdOut from a process. However, the documentation notes that, "A deadlock condition results if the parent process calls p.StandardOutput.ReadToEnd followed by p.StandardError.ReadToEnd and the child process writes enough text to fill its error stream. The parent process would wait indefinitely for the child process to close its StandardOutput stream. The child process would wait indefinitely for the parent to read from the full StandardError stream. 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.". Below is some code to avoid the deadlock.
using System;
using System.Diagnostics;
namespace NS
{
/// <summary>
/// This class handles stderr and stdout for a process.
/// </summary>
public class ProcessOutputHandler
{
private Process proc;
/// <summary>
/// The constructor requires a reference to the process that will be read.
/// The process should have .RedirectStandardOutput and .RedirectStandardError set to true.
/// </summary>
/// <param name="process">The process that will have its output read by this class.</param>
public ProcessOutputHandler(Process process)
{
proc = process;
Debug.Assert(proc.StartInfo.RedirectStandardError, "RedirectStandardError must be true to use ProcessOutputHandler.");
Debug.Assert(proc.StartInfo.RedirectStandardOutput, "RedirectStandardOut must be true to use ProcessOutputHandler.");
}
/// <summary>
/// This method starts reading the standard error stream from Process.
/// </summary>
public void ReadStdErr()
{
try
{
string line;
while ((!proc.HasExited) && ((line = proc.StandardError.ReadLine()) != null))
Console.WriteLine(line);
}
catch (InvalidOperationException)
{
// The process has exited or StandardError hasn't been redirected.
}
}
/// <summary>
/// This method starts reading the standard output sream from Process.
/// </summary>
public void ReadStdOut()
{
try
{
string line;
while ((!proc.HasExited) && ((line = proc.StandardOutput.ReadLine()) != null))
Console.WriteLine(line);
}
catch (InvalidOperationException)
{
// The process has exited or StandardError hasn't been redirected.
}
}
}
private void runProcess(string FileName, string Arguments)
{
// prep process
ProcessStartInfo psi = new ProcessStartInfo(FileName, Arguments);
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
// start process
using (Process process = new Process())
{
// pass process data
process.StartInfo = psi;
// prep for multithreaded logging
ProcessOutputHandler outputHandler = new ProcessOutputHandler(process);
Thread stdOutReader = new Thread(new ThreadStart(outputHandler.ReadStdOut));
Thread stdErrReader = new Thread(new ThreadStart(outputHandler.ReadStdErr));
// start process and stream readers
process.Start();
stdOutReader.Start();
stdErrReader.Start();
// wait for process to complete
process.WaitForExit();
}
}
}
}