Reputation: 7419
I would like to have a single executable file that I can use to either open a graphical app (the default use case, when clicking on the .exe), or that I can use to run command line tasks.
Is this possible?
If so, how would I have to modify my app.xaml/app.xaml.cs so it only opens the graphical view on specific conditions (e.g. no commandline parameters)?
Upvotes: 15
Views: 6244
Reputation: 10350
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();
}
Upvotes: 7
Reputation: 475
I kept getting a bug when using the above two solutions. Described here and it was resolved in the same manner. Visual Studio 2017 Debug Error: To prevent an unsafe abort when evaluating the function *.toString all threads where allowed to run
Tools → Options → Debugging → General → Check 'Use Managed Compatibility Mode'
Continuing my search for a solution that did not require me to switch off certain debug options I found this article that worked very well for me. https://benohead.com/blog/2015/04/22/c-wpf-console-hybrid-application/
Should it ever get removed, add a Program.cs, swap Windows App to Console application and make App.Program the startup object. Then in Program.cs, add and adjust below following code. The DLL imports take care of being able to hide the console window. All credit goes to @Benohead.
I made one change to default to GUI mode, and make arguments trigger the console app. The console window will show and close shortly prior to going into GUI mode.
using System;
using System.Runtime.InteropServices;
namespace HybridApp
{
public class Program
{
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[STAThread]
public static void Main(string[] args)
{
if (args.Length == 0)
{
// GUI mode
ShowWindow(GetConsoleWindow(), 0 /*SW_HIDE*/);
App.Main();
}
else if (args.Length > 0 && args[0] == "-c")
{
// console mode
Console.WriteLine("Console mode active!");
Console.ReadLine();
}
}
}
}
Upvotes: 2
Reputation: 3788
@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.
(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.
Upvotes: 6
Reputation: 18033
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();
Upvotes: 4
Reputation: 3367
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();
}
Upvotes: 13