Tuesday, September 30, 2014

Using Console in Windows Forms

Coincidences are funny things. I recently answered an MSDN forum question about displaying data in a Console window from within a Windows Forms application (see this thread: http://social.msdn.microsoft.com/Forums/en-US/38444d15-0e1d-4baa-baf7-a692f5a41074/console-error-after-freeconsole-is-called?forum=csharpgeneral ). I had never done this before, but it intrigued me enough to fiddle around with it and be able to come up with an answer to the guy's question.

So what was the coincidence? Two days after answering this question, I was working on a little test app for sending data to a TCP port. It was a WPF test app, with a button to click for sending the data. The button click event allowed you to find a file in order to read its contents and send that data on to an open TCP port. I used a separate class to open a port and listen for a connection to it. In that separate class, I wanted to display connection information and I really thought it best that it show up in a Console window instead of in a TextBox in my WPF form. I was going to create a separate Console app project just for this class when it dawned on me that it would be better/easier to use this "Console from a Form" concept that I had just messed with 2 days earlier. Awesome! And, it works fine from either WPF or WindowsForms.

Here are the details. This is not all that "robust" an implementation. I really only needed the Console for writing status information. No reading from it was necessary, nor did I need to close the Console and re-open another one later on. But, I put these capabilities in my sample that I will show you.

First, you need to use DLLImport for allocating and closing the Console. Then a method for creating the console, and lastly a method for testing these capabilities:

using System.Runtime.InteropServices; // for using DllImport attribute


// Declarations
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();


public static void CreateConsole()
{
    AllocConsole();
    // reopen stdout
    TextWriter writer = new StreamWriter(Console .OpenStandardOutput()) { AutoFlush = true };
    Console.SetOut(writer);


    TextReader reader = new StreamReader(Console .OpenStandardInput());
    Console.SetIn(reader);
    TextWriter errWriter = new StreamWriter(Console .OpenStandardError()) { AutoFlush = true };
    Console.SetError(errWriter);
}


private void TestAllocConsole()
{
    CreateConsole();
    Console.WriteLine( "This is the first line");
    Console.WriteLine( "Now enter something:");
    string s = Console.ReadLine();
    Console.WriteLine( "You entered: {0}", s);
    Console.WriteLine( "Hit Enter to close this Console and another will be opened." );
    Console.ReadLine();

    // This closes and releases the Console
    FreeConsole();
    // This re-creates a brand new Console instance
    CreateConsole();

    Console.WriteLine( "This is a brand new Console window!" );
    Console.WriteLine( "Enter something:");
    s = Console.ReadLine();
    Console.WriteLine( "You entered: {0}", s);
    Console.WriteLine( "Hit Enter to close this Console for good." );
    Console.ReadLine(); 


    FreeConsole();
}

There is one unsolved issue with the above functionality, and that is the use of Console.Clear(). If you issue a Console.Clear() after you have done a FreeConsole() and a CreateConsole(), it will crash (if you never issue a FreeConsole(), then the Console.Clear() has no problems and works fine). The guy who originally asked the question that started all this (his name is Dmitry), posted a new thread when he encountered this problem. In that thread, he has several ideas to try to workaround the issue. He came up with one workaround that seemed fine to me, but it was something that he wouldn't be able to use in his particular situation (read the thread, and you'll see why). You, dear Reader, may find a use for it, however. He will hopefully post another workable solution in the thread, if he finds one:  http://social.msdn.microsoft.com/Forums/vstudio/en-US/823a024a-05ba-43b7-a72e-220223085835/console-ioexception-error-on-consoleclear?forum=csharpgeneral