Wpf and commandline app in the same executable

As @BrunoKlein suggested, I remove the StartupUri property from App.xml and then override the OnStartup method. However I use AttachConsole instead, as I found that AllocConsole caused an extra console window to appear when run from command prompt.

It is also important to call FreeConsole and Shutdown to exit cleanly.

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        if (e.Args.Length == 0)
        {
            // No command line arguments, so run as normal Windows application.
            var mainWindow = new MainWindow();
            mainWindow.ShowDialog();
        }
        else
        {
            // Command-line arguments were supplied, so run in console mode.
            try
            {
                const int ATTACH_PARENT_PROCESS = -1;
                if (AttachConsole(ATTACH_PARENT_PROCESS))
                {
                    CommandLineVersionOfApp.ConsoleMain(e.Args);
                }
            }
            finally
            {
                FreeConsole();
                Shutdown();
            }
        }
    }

    [DllImport("kernel32")]
    private static extern bool AttachConsole(int dwProcessId);

    [DllImport("kernel32")]
    private static extern bool FreeConsole();
}

You can check whether the application has been executed from a console. If not, you can allocate a console dynamically:

if (GetConsoleWindow() == IntPtr.Zero)
   AllocConsole();

where

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

[DllImport("kernel32.dll")]
public static extern bool AllocConsole();

@BrunoKlein's answer will work, and I based my answer on his solution. Quoting @BrunoKlein,

First you have to use a WPF Application project and change the app.xaml so that you can override the window creation.

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication1">
    <Application.Resources>

    </Application.Resources>
</Application>

Note this is missing the StartupUri property.

Now, even simpler (this works in Visual Studio 2015 at least), go to the project properties, and change the output type from Windows Application to Console Application. This makes the project build as a console app, but still has the capabilities of a Windows Application.

Windows Application (Class Library is highlighted in this photo, select Console Application instead)

You did it! Done.

Now, instead of having a void Main(string[] args), your "main" method is the OnStautup method of your autogenerated App class:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        string[] args = e.Args;

        if (SomeConditionBasedOnTheArgs(args))
        {
            // Instantiate view, call View.Show()
        }
        else
        {
            // Process the args
        }
    }
}

Note the one difference between this answer and @BrunoKlein's answer is that this one will always "show" a console if it is run from explorer/start menu/desktop, but if run from the command line, it will run and direct all of its standard output to that console, just like any normal console application.


First you have to use a WPF Application project and change the app.xml so that you can override the window creation.

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication1">
    <Application.Resources>

    </Application.Resources>
</Application>

Note this is missing the StartupUri property.

Then, on your App.xaml.cs you can do something like this:

public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            if (condition)
            {
                var window = new MainWindow();

                window.ShowDialog();
            }
            else
            {
                AllocConsole();
            }
        }

        [DllImport("Kernel32.dll")]
        static extern void AllocConsole();
    }