Why doesn't winform open without Application.Run()? - c#

I tried to show a winform with the code below but it opens and immediately closes.
I couldn't understand the reason for this.Any ideas?
[STAThread]
static void Main()
{
try
{
AppDomain.CurrentDomain.UnhandledException += AllUnhandledExceptions;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
Test testWin = new Test();
testWin.Show();
}
catch (Exception ex)
{
Logger.Error("Main : " + ex.Message, typeof(Program));
}
}
Its works fine if I replace testWind.Show() with Application.Run(testWin).

Application.Run runs the message loop which basically handles UI events etc until all the visible windows have shut. The method blocks until the message loop has shut down.
You need that message loop to be running in order to keep the UI up, basically - whereas Show() will just display the window, but not run any kind of message loop itself, and not block - do the Main method completes, and the application terminates.

You should use ShowDialog method instead.
Test testWin = new Test();
testWin.ShowDialog();

Related

What is the proper way to exit a WinForms application from static void Main() entry point

I know there are other questions about Environment.Exit but I would like the answer in respect to the specific set of facts described here.
What is the proper way to exit the static void Main() entry point of a WinForms application if the goals are to have the application shut down cleanly, free up all memory, and leave nothing in an unstable state. Is it as simple as Environment.Exit(0)?
namespace MyApp
{
static class Program
{
[STAThread]
static void Main()
{
bool authorized=false;
authorized = Test(System.Security.Principal.WindowsIdentity.GetCurrent().Name);
if (!authorized)
{
MessageBox.Show("Sorry. Maybe next time.",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Environment.Exit(0); //?
}
else
{
Application.Run(new Form1());
}
}
}
}
There are two things that can happen in your code as written:
Test, whatever it does, returns false; your code shows a MessageBox. You don't need to do anything else, no Environment.Exit etc, just show the messagebox; the app will quit when they click OK, because there is nothing stopping the code flow from leaving Main (other than the messagebox being open)
Test returns true, your app runs and shows Form1. The app will quit when Form1 closes, because Application.Run will return.
There's nothing magic about winforms apps Main method; it's the fact that Application.Run doesn't return while the form passed into it is still open that prevents the app from exiting. In the case where you don't open Form1, you open a MessageBox instead, which also blocks the execution flow until it is dismissed

MessageBox.Show() causing WPF application to run?

Inside the App.xaml.cs of my WPF application, starting from the entry point Main() which simply looks like this:
[STAThread]
public static void Main()
{
var app = new App();
app.StartApp();
}
I experience some unexpected behavior when either of the two MessageBox's are uncommented.
protected void StartApp()
{
// uncomment this = the messagebox will show and return after 'okay' is clicked.
// The application will never start even after `this.Run()` is called?
//MessageBox.Show("Hello");
this.InitializeComponent(); //auto generated code
this.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
// uncomment this = the messagebox will show and simultaneously the application will run().
// The line below will only be hit on termination of the application
//MessageBox.Show("Hello");
this.Run();
}
At a guess, MessageBox.Show is doing something funky with the current Application context, as putting MessageBox.Show on the very first line of Main() works exactly as expected.
Application.Run() besides WPF-specific stuff calls Dispatcher.Run().
Last one runs a window message loop.
Same does native message box inside MessageBox.Show.
Hence message box calls before Application.Run() should be avoided.
Task.Run(()=>MessageBox.Show("Hello")).Wait() will produce the 'expected' result - i.e. it's message pump will not affect the execution of the calling Application.

Application.Run(), Application.Exit() and timers

I have a winforms application that I will be calling through the task scheduler to download files and insert them to a database. However it is unpredictable when the files are available so I have used timer (System.Windws.Forms.Timer) to poll for the files.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args != null && args.Length > 0)
{
Form1 f1 = new Form1();
if (f1.IsLive)
{
f1.OnLaunch(); // tests DB connection and reads and updates the expiry date list.
if (args[0] == "FullStats")
f1.FullStatsDataPump();
else if (args[0] == "OptionsStats")
{
f1.OptionsStatsTimer_Tick(null, null);
f1.OptionsStatsTimer.Start();
}
else if (args[0] == "OptionsTraded")
{
f1._optionsTradedTimer_Tick(null, null);
f1.OptionsTradedTimer.Start();
}
else
{
EmailManager.InvalidInputArgument(args[0]);
Application.Exit();
}
Application.Run();
}
}
In the above code snippet, OptionsTradedTimer / OptionsStatsTimer both poll for files and then begin processes that end with Application.Exit(). This runs perfectly but then afterwards it gets stuck in an infinite message loop. I thought that Application.Run() would be called immediately after the first timer ticks and thus when Application.Exit() eventually gets called it would end the message loop. But if I step through the code then after that Application.Exit() the program returns to the timer.tick() where it originated and then continues on to the Application.Run() at the bottom. The Application.Run() at the bottom is necessary as without it the timers will only tick once and then the app will exit.
So how do I correctly tell the application to exit? Or where should I be calling Application.Run()?
That sounds somewhat weird - perhaps you should simply move the Application.exit to a seperate procedure and just change some boolean variable if you need to exit the program. In your main program you could wait for the boolean to change and then exit the program once. Additionally, you could set the boolean variable to false at the beginning, so that you doesnt need the run.
I don't think you need to use Application.Exit(). Your Main() method is the entry point to the application; when it returns, the application exits. Generally speaking, Application.Run() is used to start up a windows form that runs until closed. Give this code a try - I haven't tested it, but it should do what you want.
using System.Linq;
using System.Threading;
class MyApplication
{
[STAThread]
static void Main(string[] args)
{
const string ARG_SHOWFORM = "ShowForm";
const string ARG_STATS = "OptionsStats";
const string ARG_TRADED = "OptionsTraded";
if (args.Contains(ARG_SHOWFORM) || args.Length == 0) {
Application.Run(new Form1()); //This will block until the form is closed.
//Make sure all your supporting logic is placed into the Form.Loaded event on the form (i.e.
//get it out of the Main() method).
return;
}
if (args.Contains(ARG_STATS))
OptionsStatsMethod();
else if (args.Contains(ARG_TRADED))
OptionsTradedMethod();
else
EmailManager.InvalidInputArgument(args[0]);
}
private void OptionsTradedMethod()
{
while (true) {
if (downloadSuccessful) //Use a method here that returns a boolean if the download succeeded.
break;
else
Thread.Sleep(DEFAULT_WAIT_TIME_MS);
}
}
private void OptionsStatsMethod()
{
while (true) {
if (downloadSuccessful) //Use a method here that returns a boolean if the download succeeded.
break;
else
Thread.Sleep(DEFAULT_WAIT_TIME_MS);
}
}
}
Can't delete the question so might as well answer it.
The problem only occurs if the timer reaches the Application.Exit() line the first time it runs. (i.e. if the file is available already when the program is run). In this case Application.Exit() is called before Application.Run() where as if the timer doesn't reach the Application.Exit() on its first run (i.e. file is not yet available) then Application.Run() gets called and Application.Exit() only gets called later.
So to solve this I just added a condition in the timers' tick methods to make sure they don't so anything on their first run.
I don't agree with restructuring the program as the way it stands I can run it through the task scheduler to download the files daily without a form and with the polling functionality and I can also run it through VS as a normal winforms app with buttons for testing, debugging and downloading files when something went wrong.
To keep your main method running(keep it's thread alive) you can use Thread.Sleep instead of Application.Run(). Try this:
Thread.Sleep(System.Threading.Timeout.Infinite);
and instead of Application.Exit() use:
Process.GetCurrentProcess().Kill();
This way it works, but consider using windows sevices for this.

Displaying a form locks up mono

I've got a mono app written in c# and executed on a Mac using "mono myapp.exe"
The app itself is a "Windows Application" when viewed from the project properties, but it doesn't always show a window. In program.cs, there is a static Main:
static void Main(string[] args) {
UserClient client = new UserClient();
client.Start(args);
}
public class UserClient {
public void Start(string[] args) {
// Talk to server, listen for instructions, etc.
....
// Launch the "Stay Alive" thread
// Just a thread that Sleeps/Loops watching for an exit command; mainly used to keep the process alive
}
}
Inside the UserClient's Start method, there is a piece of code that continuously monitors a server which gives it instructions to do things. One of the things it does is optionally displays a message using a windows form.
When the server instructs the process to display a message, it instantiates a form, displays it using frm.ShowDialog() and then after 30 seconds, a timer on the form runs Close() and the frm then gets disposed. However, when this happens, on my Mac I see an application title bar saying "mono" and a new icon on my dock bar for the mono app. After about 2 minutes the mono process in Activity Monitor shows "Not Responding." This eventually will prevent the user from logging out, shutting down, etc. (because Mac OS can't kill mono gracefully).
ON THE OTHER HAND... if the server never tells the process to display that form, everything runs fine and dandy: a dock icon never shows up (which is good!), mono title bar never shows up and the mono process continues to run happily, not preventing the system from shutting down or rebooting.
Anyone experienced this or have ideas on what's causing it? My guess is that it's a new GUI thread being created by the form which isn't ever being shutdown and is somehow causing a lockup, though I'm unsure of how to handle it.
Thanks for any suggestions.
Update:
Here's some code to easily reproduce and see this happening. I realize that this seems kind of "non-standard." Having said that, the below works perfectly in a Windows environment and provides the desired result of not showing an icon in the task area except when showing a message. Currently, using Application.Run and simply doing frm.ShowDialog() produce exactly the same result.
In the end what we need is to be able to display the form, then destroy the form and any associated icon from the dock. I suspect the GUI is starting a thread which isn't ever being disposed, which is why the dock icon remains. Is there a way to make sure the GUI thread is taken care of?
static class Program {
static void Main() {
StartupClass s = new StartupClass();
s.start();
}
}
public class StartupClass {
Thread stayAliveThread;
public void start() {
// Stay alive thread
stayAliveThread = new Thread(stayAliveLoop);
stayAliveThread.Start();
// This shows a form and would normally be used to display temporary and brief messages to the user. Close the message and you'll see the undesired functionality.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Application.Exit();
Application.ExitThread();
}
/// <summary>
/// Keep the app alive.
/// </summary>
private void stayAliveLoop() {
while (true) {
Thread.Sleep(10000);
// In the real project this method monitors the server and performs other tasks, only sometimes displaying a message.
}
}
}
I feel I'm missing several things. Most notably
[STAThread]
static void Main(string[] args) { //....
Also see this answer: Windows Forms and ShowDialog problem
I can't see anything like initializing message loop for windowed application. I.e. in windows forms case something like Application.Run(). If you do not have it, no wonder application freezes. In any case, posting more code could be helpful, as stated in comment.
In the end, I couldn't resolve this. I created a process that launched another app which displayed the message form. Not really a true answer, but the solution I had to go with.

Application Startup in C#

We have a little C# startup appplication that users launch to run the latest version of our main C# WinForms application from a network share. It's kind of a simplified ClickOnce arrangement (our IT folks won't allow us to use ClickOnce).
The startup application exits after calling Process.Start("MainApplication.exe"), but the main application can take several seconds to display, leaving the user with a blank screen.
Is there a way that the starup application can poll the OS to find out if the main aplication is running before it exits? Or some other way to handle this?
You can use Process.WaitForInputIdle() to wait until your application enteres the Idle state.
Process appProcess = Process.Start("MainApplication.exe");
appProcess.WaitForInputIdle();
From MSDN:
...you have just started a process and
want to use its main window handle,
consider using the WaitForInputIdle
method to allow the process to finish
starting, ensuring that the main
window handle has been created
Remarks Section from Process.MainWindowHandle property.
You can call Process.GetProcessByName to see if the new process has been created. The other option would be to have your main application kill the startup application once it has finished loading.
Use Davids' suggestion or alternatively you can put a splash screen in your main application. It will be just a simple Form with an image running on a separate worker thread. Put this as the first item invoked on start up. Your app can continue initializing on the main thread & after some seconds or just before your Main app finishes initialization kill the worker thread.
One way to solve this easily is to use a global event to signal the startup application that the main app has reached a predetermined state. To do this create a named event handle in the startup application and wait for it to be signaled:
static void Main(string[] args)
{
const string globalName = "MyProgramName";//something unique
bool isNew = false;
ManualResetEvent mreExited = new ManualResetEvent(false);
EventWaitHandle mreStarted = new EventWaitHandle(false, EventResetMode.ManualReset, globalName, out isNew);
try
{
if (!isNew)//already running, just exit?
return;
//start and monitor for exit
Process pstarted = Process.Start("...");
pstarted.Exited += delegate(object o, EventArgs e) { mreExited.Set(); };
pstarted.EnableRaisingEvents = true;
int index = WaitHandle.WaitAny(new WaitHandle[] { mreExited, mreStarted });
if (index == 0)//mreExited signaled
throw new ApplicationException("Failed to start application.");
}
finally
{
mreExited.Close();
mreStarted.Close();
}
}
Then in the main program you signal the event once your ready for the startup application to quit:
static void Main(string[] args)
{
const string globalName = "MyProgramName";//same unique name
bool isNew = false;
EventWaitHandle mreStarted = new EventWaitHandle(false, EventResetMode.ManualReset, globalName, out isNew);
try
{
if (!isNew)
mreStarted.Set();
Application.Run(new Form());
}
finally
{
mreStarted.Close();
}
}
I think David's second option is the way to go here. The process may be running with no visible UI yet. So, once the main form is displayed in the second app, kill the first process. You could even, conceivably, pass the process id of the original process in via StartInfo when you start the new process.
There are many trade-offs involved in this, but you may be able to make start-up time fast enough to make this issue moot by using NGEN.
My suggestion is to put Splash Screen in your application it gives you a good feel rather than if you dont want to use Splash screen then kill the process when loading is finished. Or you can use Process.GetProcessByName()
For Splash Screen just make a Window Screen and in startup class just inherit the class from Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase that gives you a method OnCreateSplashScreen() Override this method like this :-
protected override void OnCreateSplashScreen()
{
base.OnCreateSplashScreen();
//yourSplashScreen is the screen you made as a window form either it would be a image or form
SplashScreen = yourSplashScreen();
}
Thanks ...Saurabh Singh Happy To Code

Categories