Thursday, March 09, 2006

Console WinForms application

Today at work one of my coworkers asked me if I knew how to create a .NET WinForms application that when invoked with command line arguments would act like a console application (no window, console input/output). At first it seemed like it should not be very hard.

First attempt.
We have created a console application that when invoked with empty arguments list would show a window. Almost good, the problem was that the console window reminded open and what's worse it showed up on top of our form.

Second attempt.
We have created a Windows Application and tried to use Console.WriteLine() to write to the console. It didn't work... It looks like Console class when used in an application that is not attached to a console redirects all writes to Stream.Null. Even bigger problem was that Console.ReadLine() throws an exception because StdInput handle was invalid.

Third attempt (Win32 magic)
After two failures we tried more pragmatic approach (google is your friend ;).
First, how does Windows know that an application is a console app. The answer lies in Subsystem field of an image Portable Executable (PE) header. For most Win32 applications two values are used: WindowsGUI and WindowsCUI (console).

The difference is that for CUI apps Windows creates a new console if a process did not inherit the console from its parent. Moreover, cmd.exe waits for the console applications to exit before it shows the next line of a command prompt.

There are also a few Win32 API functions that allow you to do some cool things with consoles even in GUI applications.

BOOL AllocConsole(void);
Creates a new console.

BOOL FreeConsole(void);
Detaches the calling process from its console.

BOOL AttachConsole(DWORD dwProcessId);
Attaches the calling process to the console of the specified process. (WinXP+ only)
ATTACH_PARENT_PROCESS can be used to attach to a parent process console.

After learning the above we had three options.
1) Create a GUI application and create a console when needed using AllocConsole. This could work but... it would always create a new console. We could use AttachConsole but it is available only on WinXP and still there is no way to force cmd.exe to wait for our process to exit.

2) Create a console app and when we decide a console is not needed use FreeConsole to get rid of it. Unfortunatelly even if we call FreeConsole right at the beginning of the Main method, console window will appear for a second or so, and it does not look good.

3) Create two separate applications to support console and GUI mode. There is a clever trick you can use so they have the same name. Check out the following article:

If you know a different way of creating a dual mode (Console/GUI) application, let me know.