Hmmmmm…… What is a hybrid application WPF? I tried to find some other term for this problem, but unfortunately it seems to be most relevant. Concept of a hybrid application WPF I will call the application, that works and presents the results in the console, as well as in its own window. Additionally, the application should support passing parameters during the boot.

Analysing the submitted requirements we can expect the following scenarios:

  • we want to run the application in graphic mode without specifying the parameters,
  • we want to run the application in graphic mode with additional parameters,
  • we want to run the application in console mode without specifying the parameters,
  • we want to run the application in console mode with additional parameters.

Analysing further we have to doing the following situations:

  • application will be launched in a console window,
  • application will be launched from outside the console window.

Such huge list of possible situations suggests, that we should think about more detailed specification of requirements. It seams, that some features are not needed.

And indeed. This number of cases was reduced to the following two scenarios:

  • in any situation, when running an application, without parameters, it runs in its own window,
  • in a situation, if some parameters are provided application must launch in a console.

Knowing the exact requirements, you can now proceed to implement.

The first issue, with which we meet is a problem reading the parameters passed to the program. If we open the method Main() in WPF we will see the following code:

/// <summary>
/// Application Entry Point.
/// </summary>
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()
{
    WpfApplication1.App app = new WpfApplication1.App();
    app.InitializeComponent();
    app.Run();
}

Lets try at the beginning, to change the header on this well-known methods of programs written for the console:

/// <summary>
/// Application Entry Point.
/// </summary>
[STAThreadAttribute]
[System.Diagnostics.DebuggerNonUserCodeAttribute]
public static void Main(string[] args)
{
    WpfApplication1.App app = new WpfApplication1.App();
    app.InitializeComponent();
    app.Run();
}

As it turns out the idea turns out to be hit. Along the way, we encounter yet another problem with compilation. We get the following error:

Error 1 Program …\WpfApplication1\WpfApplication1\obj\x86\Debug\WpfApplication1.exe' has
more than one entry point defined: 'WpfApplication1.Program.Main()'. Compile with /main
 to specify the type that contains the entry point. …\WpfApplication1\WpfApplication1
\Program.cs 15 28 WpfApplication1

Error 2 Program …\WpfApplication1\WpfApplication1\obj\x86\Debug\WpfApplication1.exe' has
 more than one entry point defined: 'WpfApplication1.App.Main()'. Compile with /main
 to specify the type that contains the entry point. …\WpfApplication1\WpfApplication1
\obj\x86\Debug\App.g.cs 61 28 WpfApplication1

About how to deal with this error you can read in my other entry – Missing Main() method?

After solving all the problems, method Main(string[] args) looks like this:

/// <summary>
/// Application Entry Point.
/// </summary>
[STAThreadAttribute]
[System.Diagnostics.DebuggerNonUserCodeAttribute]
public static void Main(string[] args)
{
    if (args.Length == 0)
    {
        WpfApplication1.App app = new WpfApplication1.App();
        app.InitializeComponent();
        app.Run();
    }
    else
    {
        // Missing code
    }
}

As you can see half of the requirements is satisfied – if you do not specify any parameters, the program will run in its own window. We need to solve only issue with console window.

In case of console we have two possible situations:

  • program is run from the console and you need only to attach to the existing console,
  • program is not launched from the console and we need to open the new console window.

In order to solve this problem, we need to check, how the program was launched. If it turns out, that console is an active process, this means, that the program was launched from the console and we just have to hook it. Otherwise, we need to create new console window:

// Get uppermost window process
IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);

// Check if it is console?
if (process.ProcessName == "cmd")
{
    // Yes – attach to active console
    AttachConsole(process.Id);
}
else
{
    // No – create new console
    AllocConsole();
}

// Program actions ...

FreeConsole();

In addition, you need to import the necessary methods:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();

[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

When we join all together we will get:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();

[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

/// <summary>
/// Application Entry Point.
/// </summary>
[STAThreadAttribute]
[System.Diagnostics.DebuggerNonUserCodeAttribute]
public static void Main(string[] args)
{
    if (args.Length == 0)
    {
        WpfApplication1.App app = new WpfApplication1.App();
        app.InitializeComponent();
        app.Run();
    }
    else
    {
        // Get uppermost window process
        IntPtr ptr = GetForegroundWindow();
        int u;
        GetWindowThreadProcessId(ptr, out u);
        Process process = Process.GetProcessById(u);

        // Check if it is console?
        if (process.ProcessName == "cmd")
        {
            // Yes – attach to active console
            AttachConsole(process.Id);
        }
        else
        {
            // No – create new console
            AllocConsole();
        }

        // Program actions ...

        FreeConsole();
    }
}