So many mistakes for me to make, so little time...capturing StandardError and StandardOutput
This struck me as particularly interesting, because it's one of those "doh!" things that is simultaneously completely obvious after the fact but not obvious on a cursory glance if you're not paying attention.
If, in C#/.NET, you wish to spawn a process of CMD.EXE /C SomeBatchFileOrSomeProgram.exe, and you wish to capture both the StandardOutput and the StandardError, well, you need to think.
If you do something like this:
Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardError = true; p.StartInfo.FileName = "test.exe"; p.Start(); string output = p.StandardError.ReadToEnd(); p.WaitForExit();
You'll only get StandardError. But of course, if you try to ReadToEnd both streams (here's where it's obvious) the first one will of course block if standard error output occurs, since you can't read from standard error until you're done with standard output, etc etc, blah blah, well you get it.
string output = p.StandardOutput.ReadToEnd(); string error = p.StandardError.ReadToEnd(); p.WaitForExit();
There are lots of code samples around that contain this bug. Of course, the bug won't happen if your spawned console process doesn't mix standard error and standard output. But, when you spawn a batch file, certainly it will spawn other processes and who knows.
This fellow at CodeProject has the right idea, although his sample wasn't created to solve this problem, but rather another.
Process Proc = Process.Start(Info);
m_StdOutReader = new RedirOutReader(Proc, Printer, Proc.StandardOutput);
m_StdOutReader.Start();
m_StdErrReader = new RedirOutReader(Proc, Printer, Proc.StandardError);m_StdErrReader.Start();
He spawns two threads, one to handle standard error and one to handle standard output, which is the alluded-to-and-recommended-but-no-one-wrote-the-sample-cause-it-would-have-been-too-hard solution to this problem.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Comments are closed.
Ended up writing a class to do this. Thought I would share it with folks.
Realized that you were one of the speakers as VSLive (I have to send you details for the other problem I had mentioned to you about the windows service running on console).
Anyways, here is the class:
using System;
using System.Diagnostics;
using System.Threading;
public class ProcessStream
{
/*
* Class to get process stdout/stderr streams
* Author: SeemabK (seemabk@yahoo.com)
* Usage:
//create ProcessStream
ProcessStream myProcessStream = new ProcessStream();
//create and populate Process as needed
Process myProcess = new Process();
myProcess.StartInfo.FileName = "myexec.exe";
myProcess.StartInfo.Arguments = "-myargs";
//redirect stdout and/or stderr
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
//start Process
myProcess.Start();
//connect to ProcessStream
myProcessStream.Read(ref myProcess);
//wait for Process to end
myProcess.WaitForExit();
//get the captured output :)
string output = myProcessStream.StandardOutput;
string error = myProcessStream.StandardError;
*/
private Thread StandardOutputReader;
private Thread StandardErrorReader;
private static Process RunProcess;
private string _StandardOutput = "";
public string StandardOutput
{
get {return _StandardOutput;}
}
private string _StandardError = "";
public string StandardError
{
get {return _StandardError;}
}
public ProcessStream ()
{
Init();
}
public int Read (ref Process process)
{
try
{
Init();
RunProcess = process;
if (RunProcess.StartInfo.RedirectStandardOutput)
{
StandardOutputReader = new Thread(new ThreadStart(ReadStandardOutput));
StandardOutputReader.Start();
}
if (RunProcess.StartInfo.RedirectStandardError)
{
StandardErrorReader = new Thread(new ThreadStart(ReadStandardError));
StandardErrorReader.Start();
}
//RunProcess.WaitForExit();
if (StandardOutputReader != null)
StandardOutputReader.Join();
if (StandardErrorReader != null)
StandardErrorReader.Join();
}
catch
{}
return 1;
}
private void ReadStandardOutput ()
{
if (RunProcess != null)
_StandardOutput = RunProcess.StandardOutput.ReadToEnd();
}
private void ReadStandardError ()
{
if (RunProcess != null)
_StandardError = RunProcess.StandardError.ReadToEnd();
}
private int Init ()
{
_StandardError = "";
_StandardOutput = "";
RunProcess = null;
Stop();
return 1;
}
public int Stop ()
{
try {StandardOutputReader.Abort();}
catch {}
try {StandardErrorReader.Abort();}
catch {}
StandardOutputReader = null;
StandardErrorReader = null;
return 1;
}
}