How to make .NET application support both console and GUI mode
I am currently working on a next version of a utility to generate Visual Studio solutions. A new version will be called Solution Maker and add user interface support to a command line option. I wanted to mix console and GUI mode in a single program, but strictly speaking this is impossible. A program is assigned its type at compile time, so any attempt to change it’s behavior at execution time does not alter the application’s nature. A console application may create Windows forms, and a Window-based application may allocate consoles, but they won’t become ducks even if they quack.
But Visual Studio does it, don’t it? You can launch IDE and still you can run “devenv” from a command-line. How does it do it?
Well, actually it doesn’t! Here’s an explanation of the trick. There two binaries: devenv.com and devenv.exe. “Com” is always probed first, and this is the console one. So when you type “devenv” it’s a console version that will be executed. And if it does not get any command-line input, it simply launches devenv.exe. Smart!
And this can be done with any application. Here are the steps:
- Write an application with user interface, let’s say it’s called MyProgram.exe. This program should not have anything special.
- Write a command line application MyProgram.Command.exe. This program needs a few special lines, we will look at them.
- Rename MyProgram.Command.exe to MyProgram.com and copy it to the folder where GUI MyProgram.exe resides.
That’s it. And of course, command-line version of MyProgram needs some special code. Here it is:
class Program
{
static void Main(string[] args)
{
string relayProcessPath = null;
// Only relay to another application if no command-line arguments are specified
if (args.Length == 0)
{
string thisProcessPath = Process.GetCurrentProcess().MainModule.FileName;
if (Path.GetExtension(thisProcessPath).ToLower() == ".com")
{
// Relay process should only differ in extension
string expectedRelayPath = Path.ChangeExtension(thisProcessPath, ".exe");
if (File.Exists(expectedRelayPath))
{
relayProcessPath = expectedRelayPath;
}
}
}
if (!string.IsNullOrEmpty(relayProcessPath))
{
// Launch relay process
Process.Start(relayProcessPath);
}
else
{
// Process with a command line
Console.WriteLine("Hello from command line");
}
}
}