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(); } }
Hi, thanks for your great example. I solved the issue with the additional “ENTER”. Look at my reaction on stackoverflow:
http://stackoverflow.com/questions/1305257/using-attachconsole-user-must-hit-enter-to-get-regular-command-line/33504466#33504466
Hello,
very good example, however I also had the same issue that when I started application from the console window I had to press Enter when it was done in order to quit process. What could be the problem? I do not have Console.ReadKey() anywhere or anything else that could stop it from quiting (I basically only tried to Console.WriteLine every single argument).
Thank you!
Can you post fragment of your code. With out that it is hard to tell what is wrong.
When I try this, or a slight variation that handles creation of the console in the application_startup event, I get a disturbing result when I run my application from the command line. Specifically, I’m dropped back to a command prompt immediately, after which point my application prints its status messages. How can I prevent cmd.exe from dropping me back to the command prompt until the process ends?
Could you please send me a code of application. It will be easier to answer on your question when I will see it.
Any idea why this doesn’t work from a Visual Studio Command Prompt? Everything seems to work great from a normal command prompt.
It will work from VS. You need just add Command line arguments in Project properties -> Debug -> Start Options.
Just enter there what you want and it will open console view.
After my hybrid program exited from the command line it will pause until press ‘Enter’, have you seen this behavior?
What are the last lines of code in your main function? Maybe you have there some function that is waiting for user input from keyboard, e.g. Console.Readline?
I combine two apps the first one is console the second is wpf but…when i start hybrid app i see console and wpf window. my old console app had messages which i can’t print on the the new app! How to solve the problem?
Can you share here your main method?
Thank you!
It was exactly what I was looking for.