WPF Application exit code - c#

I am trying to set and get the application exit code .
I am trying to do something following :
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if ( e.Args.Length != 0)
{
}
else
{
new MainWindow().ShowDialog();
}
Environment.ExitCode = 110;
this.Shutdown();
}
And then I am trying in cmd to get it by echo %ERRORLEVEL%
But I get always result 0 , any idea what is the issue ?

For WPF, try
Application.Current.Shutdown(110);
Note that the application needs to be running as a console app. This answer is the easiest way I know of; the accepted answer looks more difficult.
An easy test to tell if you're running in console mode: call your app from the command line (make sure your code doesn't shut down right away). The main window should be showing. If you can type another command in the console, your app is not running in its context. The command prompt should be locked, waiting for you to close the window.

You can do it in Main method. Just change its return-value-type to int instead of void and return your exit-code
static int Main(string[] args) {
// something to do
Console.ReadKey();
return 110;
}
UPDATE:
To create a custom Main in WPF application, you should follow these steps:
First: unload the project by right-click on it in Solution Explorer
and click on Unload Project
Modify the .csproj file by change the <ApplicationDefinition Include="App.xaml"> to this one: <Page Include="App.xaml">
Now you can create your own Main method in your project:
Sample Main method and App class:
public partial class App : Application {
[STAThread]
public static int Main() {
App app = new App();
app.InitializeComponent();
var i = app.Run();
return i;
}
public App() : base() { }
protected override void OnExit(ExitEventArgs e) {
e.ApplicationExitCode = 110;
base.OnExit(e);
}
}

override the OnExit method, and in the ExitEventArgs you can set that value.
protected override void OnExit(ExitEventArgs e)
{
e.ApplicationExitCode = your_value;
}

It works for me with either method (Environment.ExitCode=110 or Environment.Exit(110)). I hope you are calling the program from the console and not from Visual Studio to then check the ExitCode...

Do it like this:
Environment.Exit(110);
This will terminate the current application with exit code 110.

You can do this in the following ways...
Application.Current.Shutdown(110);
Environment.Exit(10);
this.Close();
Shoutdown() returns a code. and Exit() also returns an exit code, but Close() only closes the application.

Related

C# Add Windows Form Application To A Console Application

I have a console application doing some things in the background, and I want to make it so that the user can change some things that the console application is doing.
I want to add a Windows Form Application to just get the user input and send it to the console application to use. I have looked and couldn't find what I'm looking for
I found this question - Add GUI to Existing Code in Visual Studio - but this didn't help me much.
I have this:
bool OnOrOff = true;
but I want to check if a check box from the windows form is checked instead of seting it to true like this:
on Windows Form the checkbox is named CheckOnOrOff and it is checked.
bool OnOrOff = CheckOnOrOff.Checked();
I assume that the user can change the settings while the console application is running and the effect should be taken immediately. Adding your winforms application as reference in console will not help since it's will be a different application. So this is what I suggest:
Make a new winforms application and change the output type from 'Windows Application' to 'Console Application' so we can see the console. Port your console logic proccess to the winforms project
Add a new static class which will hold flag between winforms and console. Example:
namespace FormWithConsole {
public static class SharedData {
public static bool Feature01 { get; set; }
}
}
Add a checkbox your Windows Form and add code bellow to the checkbox changed event:
private void checkBox1_CheckedChanged(object sender, EventArgs e) {
SharedData.Feature01 = checkBox1.Checked;
}
Add a button to start your console process, and use thread to start your console process as follow:
Thread thread;
private void button1_Click(object sender, EventArgs e) {
if (thread != null) {
try {
Console.WriteLine("Aborting current process");
thread.Abort();
thread = null
}
catch (ThreadAbortException) { }
}
ConsoleProcess process = new ConsoleProcess();
thread = new Thread(process.StartProcess);
thread.IsBackground = true;
thread.Start();
}
This is ConsoleProcess class, which hold your console logic
class ConsoleProcess {
public void StartProcess() {
while (true) {
if (SharedData.Feature01) {
// Do something here
}
Console.WriteLine("Regular process here...");
}
}
}
If you want the form minimized to system tray refer to minimize app to system tray
I think you should design a database to store the user input. Your console project and window project will run and manage by this database.
You can take input from windows form (by User) and then pass it to Console application by using parameter argument in Console Application.
The parameter of the Main method is a String array that represents the command-line arguments
So, if I had a console program (MyCApp.exe) like this:
class Program
{
static void Main(string[] args)
{
foreach (var arg in args)
{
Console.WriteLine(arg);
}
}
}
That I started at the command line like this:
MyCApp.exe Arg1 Arg2 Arg3 The Main method would be passed an array
that contained three strings: "Arg1", "Arg2", "Arg3".
If you need to pass an argument that contains a space then wrap it in quotes. For example:
MyCApp.exe "Arg 1" "Arg 2" "Arg 3"
Command line arguments commonly get used when you need to pass information to your application at runtime. For example if you were writing a program that pass basic information to copies a file from one location to another you would probably pass the two locations as command line arguments. For example:
MyCApp.exe C:\file1.txt C:\file2.txt Copyit
Here 'C:\file1.txt' is first Argument, 'C:\file2.txt' is first Argument, 'Copyit' is third Argument

Why this winform does not show up after Hide is called on its second iteration?

My current situation is as follow:
Application A injects my library into Application B
Application A then uses GetProcessAddress and GetModule to call the OnStart method in my library
Within my OnStart/OnStop method I initiate/hide a winform, in context its something like this:
private static PluginManager _pluginManager;
public static void OnStart()
{
// some log file code here
if (_pluginManager == null)
{
_pluginManager = new PluginManager();
}
// some log file code here
_pluginManager.Show();
// some log file code here
}
public static void OnStop()
{
if (_pluginManager != null)
{
// some log file code here
_pluginManager.Hide();
// some log file code here
}
}
The PluginManager winform close event is override to call Hide() so technically(I assume) my winform will never be disposed/closed?
Once application A is done its tasks and will close, it uses GetProcessAddress and GetModule to call the OnStop method of my library
Everything works as expected so far the winform shows up at OnStart and hides on OnStop as expected.
Now when I start Application A again and it calls the OnStart method, the winform never shows up, I don't get any error messages nor anything it simple doesn't open.
I know the function is called because it outputs to the log file the line before and the line after it calls Show().
As the title says, why the winform doesn't show up in the second iteration?
What else can I do to find the issue?
On further tests I found out that if I dispose of the winform and reinitialize it, the winform will work for every call:
public static void OnStart()
{
// some log file code here
if (_pluginManager == null)
{
_pluginManager = new PluginManager();
}
else
{
_pluginManager.Dispose();
_pluginManager = new PluginManager();
}
// some log file code here
_pluginManager.Show();
// some log file code here
}
However I am still unaware of why this is required given the winform is never disposed of with the initial code.
My case is probably off-topic? As I can't provide you with a reproducible code of the issue and unhappily the above is all I could collect, but hopefully some one will be able to point me in the right direction or where to look at.
This is probably a threading problem. You should only call .Show() from an [STAThread] and you should call .Hide() from the same thread that called .Show(). So do this:
private static ISynchronizeInvoke _invoker = null;
public static void OnStart()
{
_invoker.Invoke((Action)(() => {
// some log file code here
if (_pluginManager == null)
{
_pluginManager = new PluginManager();
}
// some log file code here
_pluginManager.Show();
// some log file code here
}), null);
}
public static void OnStop()
{
if (_pluginManager != null)
{
// some log file code here
_pluginManager.Invoke((Action)(() => _pluginManager.Hide()));
// some log file code here
}
}
I don't know how the application's main is structured, but you can fill in _invoker from any open form.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new Form1()); <-- This is the usual startup
// Instead, do this to grab an object to invoke on
Form1 form1 = new Form1();
_invoker = form1;
Application.Run(form1);
}
You could also create a wholly separate STAThread and call Application.Run(_pluginManager) instead of pluginManager.Show().
You have mentioned that your PluginManager winform close event is override to call Hide(), did you set e.Cancel = true?
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
base.OnClosing(e);
}

Where in the caliburn micro bootstrapper can I inject already running code and close application

I have the following code snippet.
bool ok;
var m = new System.Threading.Mutex(true, "Application", out ok);
if (!ok)
{
MessageBox.Show("Another instance is already running.", ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString());
}
That should check if the application is already running. I was trying to inject it into the
protected override void StartRuntime()
but didnt know how to kill the app in the if(!ok) branch, is there a better place to inject this code to ensure the application only run once.
protected override void OnStartup(object sender, StartupEventArgs e)
{
Application.Current.Shutdown();
}
I solved it like this.
protected override void StartRuntime()
{
bool ok;
var m = new System.Threading.Mutex(true, "Application", out ok);
if (!ok)
{
Application.Current.Shutdown();
}
base.StartRuntime();
}
Another option would be to check in the Main() method of the program, even before give WPF (or whatever) a chance to initialize. If the check returns that the application can start, then you create the Application object and make it Run().

How to start WPF based on Arguments

I'm currently developing an application that does some file manipulation and I want to be able to do the manipulation through the console or via an UI (I chose WPF).
I pretty much want to say: (psuedo)
if ( Environment.GetCommandLineArgs().Length > 0 )
{
//Do not Open WPF UI, Instead do manipulate based
//on the arguments passed in
}
else
{
//Open the WPF UI
}
I've read about a few different ways of starting the WPF Window/application programmatically like:
Application app = new Application ();
app.Run(new Window1());
But I'm not entirely sure I want to just plug this into a Console Application.
Does anyone have best practices or recommendations on how I can achieve this? The main processing functionality is in a Helper class I created. So basically I either want a static start method (like standard Console Application creates) or the UI to access the Helper class depending on the arguments passed in.
In Application class there is an event "StartUp" you can use it . It provide you the args you provide through command prompt. Here is an example from MSDN:
App.xaml
<Application x:Class="WpfApplication99.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
</Application>
App.xaml.cs
public partial class App : Application
{
void App_Startup(object sender, StartupEventArgs e)
{
// Application is running
// Process command line args
bool startMinimized = false;
for (int i = 0; i != e.Args.Length; ++i)
{
if (e.Args[i] == "/StartMinimized")
{
startMinimized = true;
}
}
// Create main application window, starting minimized if specified
MainWindow mainWindow = new MainWindow();
if (startMinimized)
{
mainWindow.WindowState = WindowState.Minimized;
}
mainWindow.Show();
}
}
I hope this will help.
There are 2 options to get the command line arguments
1) If you want to read the arguments OnStartup. This is good for global access of the args.
Override OnStartup in App.xaml.cs and look at the Args property of the StartupEventArgs class.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
foreach (string arg in e.Args)
{
// TODO: whatever
}
base.OnStartup(e);
}
}
2) Another easy way is to read the arguments from the Environment Object.
Environment.GetCommandLineArgs();
This can be used from anywhere in the application like from the Form / Page also.
I Prefer override OnStartup because the Startup event is usually registered in "App.xaml" and I sometimes don't want to modify it. And OnStartup function can provide a way to do some preprocess before Startup event is invoked. This is why we can override OnStartup!

How do I handle Command Line Arguments in Winforms if I don't want to load Main form?

I want to create an app that behaves as follows:
On no argument it displays the main form
On argument "a" does a job but the main form isn't loaded.
On argument "b" the form loads using the argument passed (load that document)
For the 1 and 3 I can handle the arguments in the form's constructor as follows:
public ConfigurationActionManagerForm()
{
InitializeComponent();
Environment.GetCommandLineArgs();
// do stuff with that argument
}
But this approach doesn't allow me to apply the behavior of 2. in the list.
In program.cs I can edit it to handle the arguments before the form is even created, but what is the correct approach on using Application.Run() if I don't want to pass a form? How am I going to inform Program class instance that I need to terminate or show a message that something went wrong or even show a little taskbar icon that the process is doing stuff (Think of it like the unzipping process).
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigurationActionManagerForm());
}
Would this approach from MSDN be correct to my application?
Do you mean in the same way that Visual Studio works?
If so then you can't do this in a normal Windows application - Visual Studio cheats.
The problem is that a Windows application can either be a Windows Forms application or a Console application, but it can't be both - its decided at compile time (for .Net applications this is in the project properties window). Your options are:
Make your application a Windows Forms application
In this case #1 and #3 will work perfecty, but for #2 you will find that you can't read from / write to the console (because there isn't one!). If your appliction doesn't need to give any feedback then this might be fine - do your work as you normally would and just don't display a form:
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
// Handle #2 here
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigurationActionManagerForm());
}
}
Make your application a console application
In this case #2 will work perfectly, however although #1 and #3 will work fine you will always have console window open in the background - if you close the console window your application will end.
Again this might be fine, but personally I find this to be a hack.
Cheat (do what Visual Studio Does)
Visual Studio cheats by having 2 separate applications - one is a Console Application and the other is a Windows Forms application. The easy solution is to leave it at that and require that users start a different executable when running the command line version (e.g. myprogram_g.exe and myprogram_w.exe).
Visual Studio goes one step further however and has a single entry point, devenv. It does this by using the fact that for compatability reasons the Windows shell will always run a .com file instead of a .exe if there is any ambiguity. Wheras all shortcuts etc.. point to the executable, if you run devenv on the command line the devenv.com application will run instead which uses magic to sort out whether or not it runs as a console or windows application.
My advice would be to create two different applications and leave it at that.
See How do I write a program that can be run either as a console or a GUI application? for more detail (make sure to read the comments which have additional useful suggestions).
Also see How to make an application as both GUI and Console application? for how ildasm does this.
You can call Application.Run() without a form instance.
That way, it will start the message loop without opening a form.
You can call MessageBox.Show() before calling .Run(), too.
You can even create and open a form, and then call Run() without specifying an argument - it just means that closing the form doesn't automatically exit the application.
E.g.
MessageBox.Show("Messaage!");
Form1 f = new Form1();
f.Show();
Application.Run();
As stated above, this way of doing Run() means that closing the forms doesn't automatically close the application. You need to handle this in the form's Close event handler. (Application.Exit())
MSDN online can help you out with this - check the help entry for Application.Run().
Basically you want a console applcation with a few changes.
Here's an example of how to get started, using a default aboutbox class:
using System;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("No Arguments");
}
else
{
if (args[0] == "a")
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AboutBox1());
}
}
}
}
}
And AboutBox1 class:
using System.Reflection;
using System.Windows.Forms;
namespace ConsoleApplication1
{
partial class AboutBox1 : Form
{
public AboutBox1()
{
InitializeComponent();
this.Text = String.Format("About {0} {0}", AssemblyTitle);
this.labelProductName.Text = AssemblyProduct;
this.labelVersion.Text = String.Format("Version {0} {0}", AssemblyVersion);
this.labelCopyright.Text = AssemblyCopyright;
this.labelCompanyName.Text = AssemblyCompany;
this.textBoxDescription.Text = AssemblyDescription;
}
#region Assembly Attribute Accessors
public string AssemblyTitle
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
if (attributes.Length > 0)
{
AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
if (titleAttribute.Title != "")
{
return titleAttribute.Title;
}
}
return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
}
}
public string AssemblyVersion
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
public string AssemblyDescription
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyDescriptionAttribute)attributes[0]).Description;
}
}
public string AssemblyProduct
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyProductAttribute)attributes[0]).Product;
}
}
public string AssemblyCopyright
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
}
}
public string AssemblyCompany
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyCompanyAttribute)attributes[0]).Company;
}
}
#endregion
private void okButton_Click(object sender, EventArgs e)
{
Close();
}
}
}
I found a neat and simple to implement solution using the example in my question provided by microsoft.
I created this application context class that is responsible for everything in the application and I use this instead of a form in the Application.Run() as shown below.
To achieve the behavior in the question, I am using a second form that is hidden and only the taskbar icon is shown. If the user wants to see how the process is doing, they can click the taskbar icon and see the logging window, which is actually the ConfigurationApplierForm in the example bellow.
class AnApplicationContext: ApplicationContext
{
private Form _currentForm;
Note the constructor is private, the main is inside this class and declared static.
private AnApplicationContext()
{
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
// choose which form to show based on arguments
if(Environment.GetCommandLineArgs().Contains("-apply"))
{
_currentForm = new ConfigurationApplierForm();
}
else
{
_currentForm = new ConfigurationActionManagerForm();
}
// initialize the form and attach event handlers
_currentForm.FormClosed += new FormClosedEventHandler(this.OnCurrentFormClosed);
_currentForm.ShowDialog();
}
Main is here, a little bit different from the original. Notice the argument in the Run method
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// context is passed instead of a form
Application.Run(new AnApplicationContext());
}
private void OnCurrentFormClosed(object sender, EventArgs e)
{
ExitThread();
}
private void OnApplicationExit(object sender, EventArgs e)
{
/* is there anything to do when all forms are closed
and the application is going to die?*/
}
}
Also, we need to tell the project that this is the startup project.
Project Properties -> Application -> Startup Project

Categories

Resources