Using the Console
Written by Harry Fairhead   
Monday, 21 March 2011

Have you ever wanted to open a console in the middle of an application that doesn't usually support one? This article explains exactly how to master the console.

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 OS native API calls which create and destroy the console and these are used in the .NET Console application template to provide a console 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 will dispose 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. First you need to make sure you have all the necessary declarations:

using System.Runtime.InteropServices;
[DllImport("kernel32", SetLastError=true)]  
static extern bool AllocConsole();
[DllImport("kernel32", SetLastError=true)]
static extern bool AttachConsole(
uint dwProcessId);
const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;

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.

Redirection

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 application 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.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.

 

Banner


What's The Matter With Pointers?

Back in the days when C was the language of choice, pointers meant programming and vice versa. Now in the more sophisticated and abstract days of C#, and even C++, raw pointers are a facility that is  [ ... ]



C# Bit Bashing - The BitConverter

Is C# a high-level or a low-level language? It doesn't really matter - all languages are low-level when you are thinking in terms of bits, and sometimes you just can't avoid thinking in bits.


Other Articles


Last Updated ( Monday, 21 March 2011 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.