C# - Delay showing console / avoid creating console on start

I was making an application to run in the background and I thought: if an error is raised let us just create a console for input / output. It occurred to me that I had never created a new console window, I had only ever used the one started upon console application start. So this post is for you if you want to delay the creation of a console window for your application, whatever your reason might be.

I did some digging and found this post where Anthony answers how to get a console window using AllocConsole. I was thinking.. this does not possibly work for .Net 5+, but turned out it did, even for .Net 9 as I was using. For this to work your application should start without a console. You do this by changing the output type of your csproj from Exe to WinExe:

<PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
</PropertyGroup>

This will make your application not attach to a console when started. Next step is to allocate a console and start using it. For this we will use AllocConsole, GetConsoleWindow and ShowWindow. I have wrapped the logic for this in a class I called LazyLoadConsole:

public static class LazyLoadConsole
{
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool AllocConsole();

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetConsoleWindow();

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
    public static void WriteLine(string s)
    {
        ShowConsole();
        Console.WriteLine(s);
    }
    
    public static string? ReadLine()
    {
        ShowConsole();
        return Console.ReadLine();
    }
    
    public static void HideConsole()
    {
        var handle = GetConsoleWindow();
        if (handle != IntPtr.Zero)
            ShowWindow(handle, NCmdShow.SW_HIDE);
    }
    
    private static void ShowConsole()
    {
        var handle = GetConsoleWindow();

        if (handle == IntPtr.Zero)
            AllocConsole();
        else
            ShowWindow(handle, NCmdShow.SW_SHOW);
    }
}

abstract class NCmdShow
{
    public const int SW_HIDE = 0;
    public const int SW_SHOW = 5;
}

Yes I know. That is 50 lines of code!! In the above we have 3 public methods, one for writing to the console, one for reading input from the console and one for hiding the console when we no longer need it (yes this removes it from the tray bar). You can add any of the methods you miss from the Console class. Both ReadLine and WriteLine just wrap the regular Console classes' ReadLine and WriteLine, but it starts the console if it is not present. It does this by calling ShowConsole. This method checks if there is already a console window, if there is not, it allocates one using AllocConsole.

We can call the above class in the following way:

LazyLoadConsole.WriteLine("Creating console window and writing this...");
var input = LazyLoadConsole.ReadLine(); //We will input "Hello world"
LazyLoadConsole.WriteLine($"Input was: {input}");
LazyLoadConsole.HideConsole();
LazyLoadConsole.WriteLine("I was gone, but now I am back!");

This will show the following at the end:

Creating console window and writing this...
Hello world
Input was: Hello world
I was gone, but now I am back!

In between "Input was: Hello world" and "I was gone, but now I am back!" the window disappears and reappears. You can add Console.Clear(); to the HideConsole() method if you do not want to keep the previous text lines in the console after it reappears.

That is all there is to it. I had some problems with Rider spawning another console which left me confused. Keep an eye on the different console windows of your IDE!

I hope this was helpful, let me know in the comments if it was!