Exception when using secondary UI message pump for a splash screen - c#

I have encountered an odd issue with the way I am showing a splash form, that causes an InvalidAsynchronousStateException to be thrown.
First of all, here is the code for Main{} where I start the splash form:
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splash = new Thread(new ThreadStart(ShowSplash));
splash.Start();
Application.Run(new MainForm());
}
static void ShowSplash()
{
using (SplashForm splash = new SplashForm())
{
Application.Run(splash);
}
}
I am using .NET2.0, with Win XP.
During some testing where the app was left running for may hours, I noticed that the number of exceptions would occasionally increase by one or two.
(Numbers obtained by PerfMon, viewing '# of Exceps Thrown' counter.) These exceptions seem to be caught and swallowed by the runtime, because they do
not ripple up and cause anything to go wrong in the app itself. At least nothing that I can determine anyway.
I have discovered that the exception is thrown when the UserPreferenceChanged event is fired by the system. Since finding this out, I can generate the exception
at will by changing the background picture or screen saver, etc.
I am not explicitly subscribing to this event myself anywhere in code, but I understand (via the power of Google) that all top level controls and forms subscribe
to this event automatically.
I still have not determined why this event is being fired in the first place, as it appears to happen while the app is running over night, but I guess that is another mystery to be solved.
Now, if I stop the splash form thread from running, the exception disappears. Run the thread, it comes back. So, it appears that something is not unsubscribing from the event, and this is causing the subsequent exception perhaps?
Interestingly, if I substitute my splash form with a default, out of the box Form, the problem still remains:
static void ShowSplash()
{
using (Form splash = new Form())
{
Application.Run(splash);
}
}
While this form is being displayed, any UserPreferenceChanged events do not cause any exceptions. As soon as the form is closed, and the thread exits, exceptions will be thrown.
Further research has lead me to this Microsoft article, that contains the following comment:
Common causes are a splash screens
created on a secondary UI thread or
any controls created on worker
threads.
Hmm, guilty as charged by the looks of it. Note that my app is not freezing or doing anything untoward though.
At the moment, this is more of a curiosity than anything else, but I am conecerned that there may be some hidden nasties here waiting to bite in the future.
To me, it looks like the form or the message pump started by Application.Run is not cleaning up properly when it terminates.
Any thoughts?

Yes, you are running afoul with the SystemEvents class. That class creates a hidden window that listens to system events. Particularly the UserPreferenceChanged event, a lot of controls use that event to know when they need to repaint themselves because the system colors were changed.
Problem is, the initialization code that creates the window is very sensitive to the apartment state of the thread that calls it. Which in your case is wrong, you didn't call Thread.SetApartmentState() to switch to STA. That's very important for threads that display a UI.
Beware that your workaround isn't actually a fix, the system events will be raised on the wrong thread. Your splash thread instead of the UI thread of your program. You'll still get random and extremely hard to diagnose failure when an actual system event gets fired. Most infamously when the user locks the workstation, the program deadlocks when it is unlocked again.
I think calling Thread.SetApartmentState() should fix your problem. Not 100% sure, these UI thread interactions are very difficult to analyze and I haven't gotten this wrong yet. Note that .NET already has very solid support for splash screens. It definitely gets details like this right.

I was able to simulate your problem and I can offer a work around, but there might be a better option out there since this was the first time I had come across this.
One option to avoid the exception is to not actuall close the splash screen, but rather just hide it. Something like this
public partial class SplashForm : Form
{
public SplashForm()
{
InitializeComponent();
}
// Not shown here, this is wired to the FormClosing event!!!
private void SplashForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
this.Hide();
}
}
Then it will be important that you make the thread that you run the splash screen on a background thread to ensure that the application is not kept alive by the splash screen thread. So your code would look something like this
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splash = new Thread(new ThreadStart(ShowSplash));
splash.IsBackground = true;
splash.Start();
Application.Run(new MainForm());
}
static void ShowSplash()
{
using (SplashForm splash = new SplashForm())
{
Application.Run(splash);
}
}

Related

Splash stays on screen if a race condition occurs

Recently I modified an application adding a splash screen.
I decided to use WindowsFormsApplicationBase so I put all the heavy initialization logic inside
protected override void OnCreateMainForm()
On some computers all successive executions of the application after the first one cause the splash screen to be visible forever and main form is not displayed.
I searched on internet to find a solution but it seems that after some bug fixes WindowsFormsApplicationBase is currently well trusted.
So I did the educated guess that the race condition which causes my splash to remain on screen is related to something specific of my application.
During initialization I write some progress signs on the splash with the following method:
public void showCurrentLoadStep(string message)
{
if (this.mySplash.IsHandleCreated)
{
this.mySplash.Invoke(new EventHandler(delegate
{
mySplash.label1.Text = message;
}));
}
}
but I think this is ok.
Besides the last operation done in the MainForm constructor is
timer1.Enabled = true;
I verified that the timer callback is executed also when the splash remains and MainForm is not displayed.
What I'm going to try is to move timer1.Enabled = true; after the creation of MainForm but I would like to understand what goes wrong because, as I have already said, this race condition happens only on some computers so if I don't see it any more I cannot say I have solved it.
Update: I'm using .net framework 4.0 extended
Why are you so sure that this is a race condition?
You need to supply much more code in order for someone not familiar with your application to be able to help you. When are the procedures called, when is the splash dialog initialized, is it from the main form etc.

Trying to make my windows form lighter

I have a form that contains so many controls and does lots and lots of interactive actions. This is causing my form to have some delays during usage.
One of these controls is a plotting tool that plots lots of data recieved from my server. I thought of moving the ploting tool to another form trying to make my form lighter, reducing the delay problem. I have been told by a friend that this won't help much since it is the same thread handling both of the forms, is that true ?
What your friend says is true but it is unlikely to apply here. A form appears sluggish when it has a lot of controls. When it needs to redraw itself then you'll start noticing the time taken by every control to paint itself. Typically that happens when the form has around 50 controls, but greatly depends on the type of control. Buttons are very expensive, labels are not, for example. Your plot is likely to be expensive so anything that's drawn after it (higher in the Z-order) will be delayed. Try right-clicking the control and click "Bring to Front" so it is drawn last.
Whatever you do, never just make drastic changes like you are contemplating without knowing that you'll improve your program. Which requires measuring first. You'll need a profiler to know where the cpu cycles are being consumed. Profiling painting code is not so easy, given that it doesn't execute very often. That can be fixed, modify your form constructor so it looks like this:
public Form1() {
InitializeComponent();
Application.Idle += new EventHandler((s, ea) => this.Invalidate());
}
Your form now burns 100% core, repainting itself over and over again. But is otherwise still completely functional. Just what you need to effectively profile the painting code.
If you create the form (plotting tool container) on application start, then your start speed well be down...
Then you have 2 way:
1) Move plotting tool container to a new form, but create it when it is require (after application started)
2) Move plotting tool to a new thread. in this case you can move it in an another form and create it by a new thread. so if you use this
way your start speed will be go up
Starting from the answer given here
I've just tried the code and slightly modified it to make it clearer:
static void Main()
{
Thread t1 = new Thread(Main1);
Thread t2 = new Thread(Main2);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
static void Main1()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void Main2()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form2());
}
I can say it works because I tried using a Thread.Sleep() in one of them and the second form gui didn't lock.

Exiting c# app from splash screen

I am using a splash screen for a c# which runs on startup and checks the app license.
I show the splash from the main form like this:
public partial class Form1 : Form
{
static bool stopThreads = false;
static bool gridChanged = false;
public Form1()
{
InitializeComponent();
Thread th = new Thread(new ThreadStart(DoSplash));
th.Start();
th.Join();
}
private void DoSplash()
{
Splash sp = new Splash();
sp.ShowDialog();
}
Now from the splash form I am trying exit the application when the license is invalid, but it only exits the splash and it enters the main form.
I tried exiting with :
Enviroment.Exit();
Application.Exit();
Form1 f = new Form1();
this.Close();
But none closes the main form, only the splash.
How can I close the entire app from the splash form class?
Try Application.ExitThread()
Yes, these calls only cause the thread to exit. You created a new thread. There's little point to be gracious about it in this case, Environment.Exit(1) will get the job done. The huff-and-puff version is Control.BeginInvoke() to run code on the main UI thread. You'll need a reference to Form1 to make that call.
Btw, you'll also have a big problem with SystemEvents, they run on the wrong thread because the very first window you created was created on thread other than the main UI thread. The most typical mishap is a deadlock when you lock and unlock the work station. You'll need to wait until at least one window is created on the UI thread. Form1's OnLoad() method override or Load event would be a good place to start the splash. Or just use the built-in support for splash screens.
You could use Application.Exit() or Environment.Exit().
These probably aren't the "cleanest" way to shut down your app, but if you're just bailing at the splash screen, it's unlikely it'll cause any issues.
Edit: If you want to quit without showing the splash screen at all if the licence is invalid, you should check the licence before showing the splash screen, and just exit before then.
Never introduce multithreading in application unless absolutely necessary.
As Sir Walter put it,
Else thou shalt enter into a world of pain.
Moreover, any UI interactions, such as displaying a window or working with controls, must be done on main thread only.
If you want to do something while the form is on the screen, call Show instead of modal ShowDialog so execution does not get blocked.
Application.Exit ()
will do nicely if you call it on the main thread, as you should.
If you want to show splash screen before main form is shown, you should not do it in main form's InitializeComponent. Instead, change code in Program.cs to show splash screen first:
Application.Run (new SplashScreenForm ());
Somewhere in SplashScreenForm (I don't know why you need it at all, honestly) you should check for license, and if it's fine, close the window, create MainForm instance and call its ShowDialog. If it's bad—just close the window, and since it was the last form, application would stop.

Formless desktop app - SystemEvents.SessionEnding not working all the time

I have an app that doesn't show any forms most of the time, and I needed it to close by itself when a user was shutting down the system.
So I created a question, and the answer there, SystemEvents.SessionEnding, seemed to work.
At first.
Here is my Program.Main() method:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
#region Pre-launch stuff
// Are we upgrading from an older version? We need to grab our old settings!
if (Properties.Settings.Default.UpgradeSettings)
{
Properties.Settings.Default.Upgrade();
Properties.Settings.Default.UpgradeSettings = false;
Properties.Settings.Default.Save();
}
// Visual styles
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
#if !DEBUG
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(ErrorHandling.Application_ThreadException);
// Set the unhandled exception mode to force all Windows Forms errors to go through our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(ErrorHandling.CurrentDomain_UnhandledException);
#endif
// Since we have no forms open we need to watch for the shutdown event; otherwise we're blocking it
SystemEvents.SessionEnding += new SessionEndingEventHandler(SystemEvents_SessionEnding);
// ...
// ...
// ...
Application.Run();
}
The method SystemEvents_SessionEnding runs a method calling Exit(), which in turn runs:
public static void Exit()
{
MessageBox.Show("test");
try
{
IconHandler.SuperNotifyIcon.Dispose();
}
finally
{
Application.Exit();
}
}
And yet, my app blocks shutdown at times. And the message box that I added there doesn't show up.
I can't figure out how such a simple, simple path of execution could fail. But it does, and it's a major irritant for both me and my app's users.
Any ideas? Thoughts to throw around?
EDIT:
Per corvuscorax's answer, I've tried adding a form, but it's acted weird - at first, I had this in the form's code:
ShowInTaskbar = false;
WindowState = FormWindowState.Minimized;
Shown += (sender, e) => Hide();
And modified the Program.Exit() method to close that form, which would run the disposal code as well in the FormClosing event.
It turned out disabling ShowInTaskbar stopped the form from receiving a HWND_BROADCAST message. I've commented out that line now, but even so, I found that shutdown blocking was occurring. Once I clicked a button I made to show the form and tried to shut down again, it completed smoothly.
I would recommend against not having any forms at all.
Just do a normal WinForms application and set the main Form to be hidden and you get all the message processing working as always.
Addendum
As an added bonus of doing it this way, you also get a console for free while developing - by not hiding the main form, you can have an easy output view, simulate input, trigger actions with buttons, all sorts of useful stuff.
Addendum 2
Here's how I normally create invisible main forms. In the forms constructor, add this:
if (!Debugger.IsAttached)
{
// Prevent the window from showing up in the task bar AND when Alt-tabbing
ShowInTaskbar = false;
FormBorderStyle = FormBorderStyle.FixedToolWindow;
// Move it off-screen
StartPosition = FormStartPosition.Manual;
Location = new Point(SystemInformation.VirtualScreen.Right+10, SystemInformation.VirtualScreen.Bottom+10);
Size = new System.Drawing.Size(1, 1);
}
This will hide the window when not running from within the debugger.
Does your code have a message pump?
You need a message pump inside each user interface thread of your application so that any windows messages aimed at your application are dispatched and processed. Even if you never show a Form on the screen if the thread is a user interface thread when it will still be sent other system messages that would need to be dispatched and processed.
In a typical WinForms applications the message pump is inside this call...
Application.Run(new MyForm());
...which only exits once the MyForm instance is closed. In your case it seems you never make a call to the Application.Run method or anything similar. In which case your not handling windows messages. So what is your processing loop when you are not showing a Form?

How to prevent leaving an Icon in System Tray on exit?

My program puts an icon in the system tray because the user may minimize to it. However, if the application crashes, or I stop the app from running in VS it leaves the icon in it until I hover over it with the mouse. Sometimes I'll look down there and there will be 10 or so icons.
I can I make sure the icon goes away?
There is no way to do this. Windows does not poll your program to see if it's still alive. Therefore, your icon will stay in the system tray until you either tell it to leave (normal exit), or the user does something that initiates a call to your program (such as mouseover). Only then does the shell know that your program has died and it needs to clean up your icon.
You have several options:
1) Write a root structured exception handler that ensures the destruction of the icon before exit. This one takes some effort - but you can basically add your own unhandled exception handler to Windows which will be called in the case of many forms of "boom" which will then allow you some last-ditch clean up opportunity.
2) Write a monitor application that sleeps on your main processes handle, and kills the icon when it wakes up (the main application has died). To make this latter scenario work well, you'll likely want the main process to NOT kill the icon itself - or you could create a way to tell the monitor app that it should exit w/o destroying the icon (have the monitor app sleep on both the process handle and a named event - and if woken by the event then it simply dies w/o cleaning up after the main app).
Before you exit, set the NotifyIcon Visible property to false.
You need to call Dispose on the NotifyIcon for it to leave the tray.
EDIT: There's an event you can hook into in your Program.cs. For all Windows apps, you'll have something like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
The Application class has a few events you can take advantage of to know when your app dies:
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
I don't know what happens when one kills the process, but if it crashes due to an exception, of course one can handle it. The way to do it best, depends on the type of application: Console, Forms, a service, ...
But in all cases, you should be able to use a try / finally structure in your Program.cs, and in the 'finally' section Dispose() the TrayIcon.
For example, in a Forms application, make your NotifyIcon (called TrayIcon in my example below) in your Form class public, and change the "Application.Run(new Form1())" line in Program.cs as follows:
Form form = new Form1();
try { Application.Run(form); }
finally { form.TrayIcon.Dispose(); }
We can hide trayIcon before form closing.
Add FormClosing Event
NotifyIcon ni;
...
...
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
ni.Visible = false;
}

Categories

Resources