Sunday, December 18, 2011

Redirect Console Output in C#

I've been writing some tools that will duplicate behavior that I get for free in Visual Studio's MSTest framework for MonoTouch. One of the last pieces of the puzzle was the fact that MSTest will log any output to the console as part of the test results XML file. However, it does it intelligently, so that the output for each test is saved with the test results, and any output that is emitted outside of a test goes to the console. So, how could I redirect console output temporarily?

This is a pretty simple trick, but there are a couple of pitfalls I ran into that are worth sharing. The basic idea is to use the Console.SetOut(TextWriter) method, to change the stream where calls to Console.WriteLine() will be written to. In my case, I wanted it redirected to a local variable, like so:

StringWriter sw = new StringWriter();
Console.SetOut(sw);

Subsequent calls to Console.WriteLine() will be captured by the StringWriter, and the complete output can be grabbed by calling sw.ToString() at any time.

Now, how do we recover the normal output stream we were writing to? I've seen a lot example code which calls sw.Close() to finish the stream, grabs the output with sw.ToString(), and then restores the stream with Console.SetOut(new StreamWriter(Console.OpenStandardOutput())). However, the pitfall that I experienced here was that some code that executes several times will crash on the second execution. The reason? The original output stream was garbage collected! What I had to do is hang on to a reference to Console.Out that I could use later, like so:

TextWriter stdout = Console.Out;
StringWriter sw = new StringWriter();
Console.SetOut(sw);
try
{
    //Code that will make calls to Console.Write() or Console.WriteLine()
}
finally
{
    //Needs to be in a finally block so that an exception doesn't prevent
    //you from getting stdout back
    Console.SetOut(stdout);
    string output = sw.ToString();
    sw.Close();
}

No comments:

Post a Comment