Consoling thoughts…

This article was originally published in VSJ, which is now part of Developer Fusion.
The console is a bit of a mystery to many .NET programmers. You can create a console application very easily and there is a Console class which allows you to interact with the user at a very basic level. The problems start when you are in the middle of some non-console based project and suddenly it would be useful to pop-up a console window.

You might think, given that there is a Console class, that this should be easy. All you should have to do is create an instance of Console and start using it. Of course when you look at the situation a little more carefully this isn’t possible because Console is a static class and hence there is no way of creating an instance. At this point you might be tempted to give up and program your own class using a form of some kind, but it is possible to use a console in any application including a .NET forms or WPF application.

The first thing to realise is that the console window is provided by the operating system not the .NET framework. It’s the command prompt that you use to do jobs that can’t easily be achieved via the usual GUI. There are API calls which create and destroy the console and these are used in the .NET Console application template to provide a consol for the Console class to wrap. As there can be only one console attached to a process at any one time the Console class works in a very simple way. When you reference any Console method it simply makes use of the currently attached console. So in principle if you manually create a console you should be able to use the Console static class to work with it and so avoid having to create your own wrapper.

The console API

The console API is very simple. There is an API call that will create and attach a new console:
[DllImport("kernel32", SetLastError = true)]
static extern bool AllocConsole();
If it is successful it returns true and generally the only reason for it to fail is that the process already has a console attached. If you want to discover the error code that the call generated, use the .NET method:
Marshal.GetLastWin32Error()
If a console already exists and is attached to another process you can attach it to the current process using:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(
	uint dwProcessId);
In most cases the console that you want to attach belongs to the parent process and to do this you can use the constant:
const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;
There is also a FreeConsole API call that disposes of any currently attached console:
[DllImport("kernel32.dll",
SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
There is also a long list of other console API calls but in the main you don’t need these because the Console class provides you with managed alternatives. For example, there is a Set and Get ConsoleTitle API call, but the Console property Title does the same job by calling the API for you.

Console use

Putting theory into practice is very easy. A single method is all we need to either create or attach an existing console:
public void MakeConsole() {
	if (!AttachConsole(
		ATTACH_PARENT_PROCESS)) {
		AllocConsole(); };
}
You can add some error handling to this to make it more robust but if there is an error the only consequence is that the Console class subsequently doesn’t work.

To test it out try:

MakeConsole();
Console.Beep();
Console.Title="My Console";
Console.WriteLine("Hello console, World!");
Console.Write("Press a key to continue...");
Console.Read();
This makes the console beep, changes its title and writes some suitable messages.

There is one small, subtle “gotcha” that you need to keep in mind. If you generate the console yourself then the user cannot redirect input/output from/to a file. That is, if your application is MyApp.exe then:

MyApp > MyTextFile.txt
…works if MyApp is a console app but doesn’t work if you create the console. What you have to do in this case is detect the arguments “> MyTextFile.txt” when your application creates the console. For example, to redirect the output file you would use:
public void MakeConsole() {
	if (!AttachConsole(
		ATTACH_PARENT_PROCESS)) {
		AllocConsole(); };
	string[]
	cmd=Environment.GetCommandLineArgs();
	if (cmd[1] == ">") {
		Console.Write(cmd[2]);
		FileStream fs1 =
			new FileStream(cmd[2],
			FileMode.Create);
		StreamWriter sw1 =
			new StreamWriter(fs1);
		Console.SetOut(sw1); }
	}
This uses the Environment object to retrieve the command line arguments and then tests for an output redirection in cmd[1]. If it finds a “>” it then creates a FileStream and then a StreamWriter which it sets the standard output to. From this point on everything sent to the Console is stored in the file. The only complication is that you have to remember to close the file when the application is finished with the console. For example:
Console.Title="My Console";
Console.WriteLine("Hello, World!");
Console.Out.Close();
Of course a better design would be to put the Out.Close in a finalise method. You can write similar code to redirect the standard input stream and even to form pipes and interpret other strange command line syntax.


Harry Fairhead is a consultant who advises on networking and computer hardware platforms. Contact him at [email protected].

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.”