After playing with this a bit, and seeing the caveats with AllocConsole and AttachConsole, I think the best idea is to have two .NET exe files, say foo.exe and fooconsole.exe. When you need console output, use fooconsole.exe.
All of your code other than your main function can be placed into a .NET project which produces a DLL (class library). This includes all win forms, including your main window. The only thing left in the exe project is a small main function which in turn calls a static function in your DLL. All of this can be done without resorting to P-Invoke.
In your DLL class library:
using System;
using System.Windows.Forms;
namespace MyNamespace
{
public class MainEntry
{
private static bool mIsConsole = false;
private MainEntry() { }
public static bool IsConsoleApp
{
get { return mIsConsole; }
}
public static int DoMain(string[] args, bool isConsole)
{
mIsConsole = isConsole;
try
{
// do whatever - main program execution
return 0; // "Good" DOS return code
}
catch (Exception ex)
{
if (MainEntry.IsConsoleApp)
{
Console.Error.WriteLine(ex.Message);
}
else
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return 1; // "Bad" DOS return code indicating failure
}
}
}
}
In your project which emits a windows EXE (foo.exe), this is the ONLY code:
using System;
namespace MyNamespace
{
static class Program
{
[STAThread]
static int Main(string[] args)
{
return MainEntry.DoMain(args, false);
}
}
}
In your project which emits a console EXE (fooconsole.exe), this is the ONLY code:
using System;
namespace MyNamespace
{
static class Program
{
[STAThread]
static int Main(string[] args)
{
return MainEntry.DoMain(args, true);
}
}
}
Of course, in both EXE projects, you need a reference to the DLL project in the same solution. On the Application tab of project properties, you can change the Project type - Windows App (EXE), Console App (EXE), or class library (DLL).
Note that it is possible to programatically determine if an EXE file uses the windows or console subsystem, but it is probably not worth it - lots of P-Invoke and you have to look at the bytes of the EXE file's PE header.
Also, the AllocConsole/AttachConsole methods are funny in that if you run your program from the command line or a batch file, and try to redirect the output (stdout and/or stderr) to a file, it wil not work - it will not go to the file. See http://www.nabble.com/WIN32:-Spawning-a-command-line-process-td21681465.html
Again, possible to work around, but it requires more P-Invoke and probably not worth it.
As a best practice, I never put more into an EXE project than the simple Main function that in turn calls a static function in a DLL. There are a lot of reasons, but one good reason is that unit testing tools work better with DLLs than with EXEs.
Note also the form of the Main function which takes a string array of args, and returns an int. This is the best form to use, because you can use ERRORLEVEL in batch files to work with whatever number your EXE returns - traditionally 0 for success and larger than 0 for failure.