WPF / Console Hybrid Application - c#

I writing an application what can either be run on the command line, or with a WPF UI.
[STAThread]
static void Main(string[] args)
{
// Does magic parse args and sets IsCommandLine to true if flag is present
ParseArgs(args);
if(IsCommandLine)
{
// Write a bunch of things to the console
}
else
{
var app = new App();
app.Run(new Window());
}
}
I set the project's Output type to Console Application, I get a console window that popups if I try to execute it by double-clicking the exe. I don't want to show the console window to the user if the flag is not set (passed in via command args).
However, if I set the project's Output type to Windows Application, the double-click behaviour is fine, but when I run it in the console, I get no console output (Console.Writeline)

Your best bet would be to abstract out the code that actually does the work to a separate class library that has no UI and then create two applications one Console, the other WPF that call this.
A console application and an WPF application have entirely different application models so you can't reuse the same code in both applications.
Having a separate class library allows you do other things like use it in other applications such as a web site or client/server architecture.

Create a WPF app and add the following code to your App class:
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Length > 0)
{
List<string> lowercaseArgs = e.Args.ToList().ConvertAll(x => x.ToLower());
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
// your console app code
Console.Write("\rPress any key to continue...");
Console.ReadKey();
FreeConsole();
}
Shutdown();
}
else
{
base.OnStartup(e);
}
}
private const int ATTACH_PARENT_PROCESS = -1;
[DllImport("kernel32", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool FreeConsole();
}

You can conditionally start your WPF application by performing the following steps with the sample below.
Add another entry point with the 'Main' method declarated with STAThreadAttribute. This is required by WPF.
Under your project's 'Build' properties, choose 'Console Application' as your output and your new 'Main' method as the application's 'Startup Object'.
using System;
public static class Program
{
[STAThreadAttribute]
public static void Main()
{
Console.WriteLine("What now?");
Console.ReadKey(true);
App.Main();
}
}

I know I'm a little late to the party, but figured I could toss in my two cents. You could always keep it as a console application, and then hide the console as per this answer (https://stackoverflow.com/a/3571628/1059953). There is a moment of the console being displayed, then it disappears and the window shows up.

Related

FreeConsole() does not detach the process

I have already read this similar quesiton but the solution accepted in that question is not working for me.
I have a WinForm application (called FormPlusConsoleApp) that works as a console application if some arguments are passed by the calling program.
I need to provide status messages from the program FormPlusConsoleApp to the console therefore, I attach to the console at the very beginning of the program.
PROBLEM: After executing the required job, the program should exit with an exit code 0/1 and should completely detach to the console without any user interaction.
I am already calling the FreeConsole() method as suggested in this similar quesiton but at the end, a blinking cursor appears on the command prompt (as shown in the screenshot) and the user must press a button to completely exit.
Sample Working Program:
using System;
using System.Windows.Forms;
using System.IO;
namespace FormPlusConsoleApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
System.Windows.Forms.MessageBox.Show("Attach the debugger now...");
if (args.Length == 0) //form version
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormApp());
}
else //start console version
{
// case args.Length > 0
SampleConsoleExecution();
}
}
static void SampleConsoleExecution()
{
//attach to the console
bool isAttached = AttachConsole(-1);
//Sample Message
Console.WriteLine(Environment.NewLine + "Console process started...");
//do something
Console.WriteLine(Environment.NewLine + "Exit process!");
//Free console
FreeConsole(); //<<<<<<<<<--------blinking cursor appears on the console but the user still have to press the "ENTER" button
}
//To attach to the console (parent)
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
//To free from the attached console
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
}
}
PS: I understand that the best approach is to have separate WinForm and console applications that use the same business logic. Unfortunately, that's not an option for me. The above code is just MWE to test the problem.
As written in this blog post by Raymond Chen and this answer you can't have an app that is both, console and windows app.
There are few workarounds, the approach that worked for me is to add the following code at the end of the main function:
SendKeys.SendWait("{ENTER}");
In order to set the exit code, you could use Environment.Exit:
Environment.Exit(0);

Console Application to be run as GUI as well

Before anyone marks this as Duplicate, Please read
I have a process which is Windows Application and is written in C#.
Now I have a requirement where I want to run this from console as well. Like this.
Since I need to show output to console, so I changed the application type to Console Application
Now the problem is that, whenever user launches a process from Windows Explorer (by doubling clicking). Console window also opens in behind.
Is there is way to avoid this ?
What I tried after #PatrickHofman's help.
Followed how to run a winform from console application?
But I still have problems
When I do this https://stackoverflow.com/a/279811/3722884, the console opens in new window. I don't want that.
When I do this https://stackoverflow.com/a/11058118/3722884, i.e. pass -1 to AllocConsole there are other problems which occur, as mentioned in the referred link.
OK I thought I'd have a play at this as I was curious.
First I updated the Main method in Program.cs to take arguments so I could specify -cli and get the application to run on the command line.
Second I changed the project's output type to "console application" in the project properties.
Third I added the following methods to Program.cs
private static void HideConsoleWindow()
{
var handle = GetConsoleWindow();
ShowWindow(handle, 0);
}
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
Fourth I call HideConsoleWindow as the first action in non-CLI mode.
After these steps my (basic) application looks like:
[STAThread]
static void Main(string[] args)
{
if (args.Any() && args[0] == "-cli")
{
Console.WriteLine("Console app");
}
else
{
HideConsoleWindow();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Then if I open the program on the command line with the -cli switch it runs as a command line application and prints content out to the command line and if I run it normally it loads a command line window (extremely) briefly and then loads as a normal application as you'd expect.

How to be dynamically either console application or Windows Application

I have a small application who should be executed in two modes: non UI or WPF Window. It should depend on command line arguments.
In each mode, I need to show some feedback log:
In WPF Window mode, WPF is going to take care of visualizing logs,
In no UI mode, I need a console to show logs. If my app have been started from a console (mainly cmd.exe), I'd like to use it without opening a new one. If my app have been started outside of a console (double click on explorer, a CreateProcess, ...), I need to create a new console to output my results and wait for a Readkey to close it.
I have found:
how I can create a new console:
How to open/close console window dynamically from a wpf application?,
how to get current console windows handle to show/hide it:
Show/Hide the console window of a C# console application
And I know I can statically choose between "Windows Application" or "Console Application" in project property.
Choosing "Windows Application", GetConsoleWindow() is always 0 and I don't see how to reuse a previous console.
Choosing "Console Application", I can reuse a previous console but when started from explorer in WPF Window mode, a console is created under my WPF main window.
The question is: how can an application be really dynamic? Either in WPF Window mode, with only a WPF windows (and no console at all) or in non UI, with only one console (starting one or a new created one).
It was a lot easier in Winforms, but its not too hard.
Start off with a WPF Application Project (not a console application project with WPF windows).
Create a new Program.cs class in the root directory, add the following code:
class Program
{
[DllImport("Kernel32")]
public static extern void AllocConsole();
[DllImport("Kernel32")]
public static extern void FreeConsole();
[DllImport("kernel32.dll")]
static extern bool AttachConsole(uint dwProcessId);
[STAThread]
public static void Main(string[] args)
{
bool madeConsole = false;
if (args.Length > 0 && args[0] == "console")
{
if (!AttachToConsole())
{
AllocConsole();
Console.WriteLine("Had to create a console");
madeConsole = true;
}
Console.WriteLine("Now I'm a console app!");
Console.WriteLine("Press any key to exit");
Console.ReadKey(true);
if (madeConsole)
FreeConsole();
}
else
{
WpfApplication1.App.Main();
}
}
public static bool AttachToConsole()
{
const uint ParentProcess = 0xFFFFFFFF;
if (!AttachConsole(ParentProcess))
return false;
Console.Clear();
Console.WriteLine("Attached to console!");
return true;
}
}
Now you have a console app or a WPF app. In the Properties, set the start up object as the Program.Main method. In the example above, WpfApplication1.App.Main is the old start up object (defined in the App.xaml.cs file).
Edit this misses one of your requirements about using the existing console and I will edit it as soon as I figure out how to stay in the same console window.
New Edit Now works to use the existing console!

C# hybrid cli and win form app

I have been researching how to create a hybrid winform and CLI app... I started out my app as Winforms, now I am adding CLI to it... It seems to work but has a few issues I want to figure out how to fix and have not been able to do so , probably due to my lack of experience with C#.
If i have output type in VS set to "windows application", and use the code below i am able to run the winform portion for GUI, and also from command line am able to give it parameters and it works, well at least it outputs consolewrites i have coded in, i have a seperate c# class file that has my main code so it is seperate from winform GUI and my eventual cli code, they both will just feed user input to this other "main" c# class.. anyway here is the code.
[STAThreadAttribute]
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
AttachConsole(ATTACH_PARENT_PROCESS);
cli_main cli = new cli_main();
cli.start_cli(args);
}
}
well it works in gui, i can access my menus and create different win forms, moment i click a button to perform an action i get the following exception:
System.Threading.ThreadStateException was unhandled by user code
Message=Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.
if i then change the output type to "console application", it works perfectly in all manners in cli and GUI, BUT when it opens the winform/GUI portion i get this ugly CMD window that will not go away.. here is the code i used, basically just what i started with before i added the above code.
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
cli_main cli = new cli_main();
cli.start_cli(args);
}
}
again with my lack of knowledge on C# i am hoping someone can point me to a solution . I would prefer to keep the app as output "console application" and find a way to hide the console that is opened when i start the winform/gui portion..?? thanks in advance.
Did you do what it said in the error message?
Ensure that your Main function has STAThreadAttribute marked on it
In the code you pasted, the STAThread attribute is not marking the Main method, it is marking the AttachConsole external function. Move that to where it ought to be and you shouldn't have any problems.
If your application is a console application, it will get a console window automatically if there isn't already one attached. That's the point of making it a console application. You can use FindWindow and ShowWindow(SW_HIDE) to hide it at runtime but it will still flash on-screen briefly.
If you plan to start your application from an existing console window most of the time, you should keep it as a console application, since it will inherit the parent process's console window by default. If you plan to start your application from a UI shortcut or from other non-console processes, you should probably make it a Windows application and allocate the console as needed.
Thanks for all, but some changes for previous pattern
It works so:
static void Main(string[] args)
{
// Test pattern
args = new string[] { "-flag1", "value1", "-flag2", "value2", "-flag3", "value3" };
if (args.Length == 0)
MainWinApp();
else
MainCLI(args);
}
[STAThread]
static MainWinApp()
{
// Your code for start GUI application
}
[STAThreadAttribute]
static void MainCLI(string[] args)
{
// Your code for CLI application
}

WPF window from a Console project?

I recently started a C# project (VS 2008) as a 'Console' project where I wrote a few libraries, test programs, etc. Now I'd like to add a couple of WPF windows, but it looks like console project won't let me do that. I'm coming from Java so this is a little strange. How can I add a WPF form (which I will instantiate my self from my "main" class?
The accepted answer is not entirely true, I'm afraid, just add the [STAThread] attribute before your mainmethod and make references to the right libraries (like System.Windows) and you're all set to add wpf windows.
EDIT : in the comments #JamesWilkins supplied me with this usefull link : http://code-phix.blogspot.be/2013/11/creating-wpf-project-from-scratch.html
I had the same question and was looking for a similar answer. I found info all over the place, so I'm putting what I found in one place. I also needed a way to hide and show the console window, so I found out this worked (for VS 2013+):
Create a new console project (be sure to select the .NET framework version you need to use - I needed to use .Net 4.0 myself). Make sure to have the following references:
PresentationFramework
PresentationCore
WindowsBase
System.xaml
Right-click on the project in the solution explorer, select
"Properties", and change the project Output Type to Windows Application.
This prevents the console window from showing on startup (if you
want that, skip this step).
While controlling the console window is not necessary in order to add WPF windows, it can be useful. If you don't need this, skip to #4. In the "Program" class for the console, add this in order to control the window:
public class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole(); // Create console window
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow(); // Get console window handle
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
const int SW_SHOW = 5;
This allows to create, hide, and show the console window. I created these methods to do this:
static void ShowConsole()
{
var handle = GetConsoleWindow();
if (handle == IntPtr.Zero)
AllocConsole();
else
ShowWindow(handle, SW_SHOW);
}
static void HideConsole()
{
var handle = GetConsoleWindow();
if (handle != null)
ShowWindow(handle, SW_HIDE);
}
These are mostly self explanatory, but if the project is in window mode, GetConsoleWindow(); returns null, so here we test if the handle is null (zero in this case), and if so, a console window needs to be created (only once). After this, GetConsoleWindow(); will always return a handle to use.
As stated in another answer already, you need to add [STAThread] on a line before your console's Main method. This is required, as WPF needs to run in a Single Threaded Apartment environment.
[STAThread]
static void Main(string[] args)
{
}
Adding a window: To do this, just add a user control to your project and name it "MainWindow" (or whatever you like). Just right-click the project node in the solution explorer and select Add->User Control.... Open the MainWindow.xaml.cs code behind and change MainWindow : UserControl to MainWindow : Window. Next, open the MainWindow.xaml file and change the first tag <UserControl to <Window (and make sure the closing tag gets renamed also, which should be automatic if using Visual Studio). Close all "MainWindow" editor tabs and reopen (just to be sure, may not be necessary). You should see MainWindow.xaml now show a window in the design pane.
Showing the WPF window: To do this, we need to start the window message loop, which is really easy. To begin, I created some properties to store the objects. Just put this somewhere in the Program class.
public static Application WinApp { get; private set; }
public static Window MainWindow { get; private set; }
Next we have to create the message loop by creating a System.Windows.Application object, then pass it the main window. I created this method to perform this task:
static void InitializeWindows()
{
WinApp = new Application();
WinApp.Run(MainWindow = new MainWindow()); // note: blocking call
}
and that's it! To test this, put some content in your main window and do this:
[STAThread]
static void Main(string[] args)
{
ShowConsole(); // Show the console window (for Win App projects)
Console.WriteLine("Opening window...");
InitializeWindows(); // opens the WPF window and waits here
Console.WriteLine("Exiting main...");
}
Hope that helps saves someone time, cheers! ;)
TIP: I found it helpful, in my case, to call InitializeWindows() in a new thread; however, that means that you must create UI objects (among other things) in the the same thread that the Application object was created in. To communicate with the new thread, I just used the Dispatcher class (WinApp.Dispatcher.BeginInvoke()) to run requests in the WPF thread context.
For Windows 8/10: If you are debugging and you don't see any text in your output window, take a look here: https://stackoverflow.com/a/49145317/1236397
You should move your library code to some other "Class library" project and use it from your Console project. Your WPF windows should be in another "WPF application" project which will also reference your "Class library".
Thanks to aku and Dmitriy, I create another project (WPF) which will reference my console based code.
Are you sure you need Console project? You can create 'WPF application' project and add references to your libraries, etc. If try to show WPF window from console app you will gen an exception due to differences in threading model between Console & WPF apps.

Categories