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.
Related
I just want to create a simple pause button, and perhaps another button to resume if necessary. I've been looking around and I mostly see Thread.Sleep(), which doesn't work for me because I want the pause to remain paused until the user desires.
Thread.Suspend() doesn't work because that is obsolete now.
I've also seen another solution of creating a second form, however, that doesn't seem to be working for me. Once that second form opens up, the entire program closes.
I'm not sure if this makes a difference, but my program currently uses two threads (main thread running form1 along with another thread). Ideally, everything needs to be paused.
The simpliest thing to do is to have some variable/property that the other thread can access.
Bool ShouldPause = false;
The other thread should have in it's game loop something like that :
while(true)
{
if(!ShouldPause)
{
UpdateGame();
}
Draw();
}
Then the game will proceed only when the ShouldPause variable is false.
I did it several times and it worked perfectly.
You don't want to pause the thread via some "Suspend" like functions because it will prevent from him to draw on the screen and would appear like it's not responding.
You can use Thread Signaling technique.
A good start is to take a look at ManualResetEvent Class
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.
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);
}
}
I wrote an alarm app with some complex code i dont feel like breaking up right now to use as an example. I have a timer checking every 10 or so minutes about a state online and on certain conditions my app alerts me. On form_load i set the timer to 10mins and when it triggers and the condition is true i call a function with this in it.
{
this.WindowState = FormWindowState.Maximized;
this.TopMost = true;
this.Activate();
}
When i start the app i typically minimize it and do whatever. Today i notice it isnt working. On my initial test i call the code after pulling the states and calling the func on form_load which always brought it up but now that i am doing other things and the window has been minimized i notice it did not work. How do i fix this?
Are you hiding the form? In which case try this.Show() instead.
Additionally, form_load runs once (usually). You want form_activated. The form is in memory (loaded) whether or not it's minimized.
And, including a call to the activate event in your form_load event is redundant.
I have a very strange behavior that only seems to happen on one form.
Basically I am creating an instance of a Form, and calling Show() to display the form non-blocking. In that form's Load event handler, I have some logic that may call this.Close() under certain circumstances. This closes the form, but then the form Show() method in the client code throws an ObjectDisposedException.
The stack trace from the ObjectDisposedException is as follows:
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.Form.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.ContainerControl.FocusActiveControlInternal()
at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
at System.Windows.Forms.Control.Show()
...etc.
This is what I'm seeing happen:
Control.Show() is called
my form is launched
the OnFormLoad method is called
the FormLoad event handler is called, inside of which I call this.Close()
the OnFormClosing method is called
the FormClosing event handler is called
Dispose is called on my form and all it's user controls
and then somewhere toward the end of the Control.Show() method, it tries to get a handle to the form, which freaks out and throws an exception because the object is marked disposed.
My real question is, why can I do this exact same thing on every other form I have without exceptions? Is it a GC issue? I've tried putting a GC.Collect() call right after the this.Close() and it makes no difference. Like I said, it happens 100% of the time on this form, and never anywhere else, regardless of child user controls, scope of the form variable, etc.
Any ideas?
The best way to do so :
this.BeginInvoke(new MethodInvoker(this.Close));
this is the most simple way you wont get ObjectDisposedException
I know this is an old issue but no one seemed to have posted the obvoius answer.
You say you call Control.Show() and then Form.Close() and then the form is Disposed of. Well, unless you use MDI or use ShowDialog that's just as documented. Though, the short version of the Close() documentation is "Closes the form", it actually also disposes it implicitly under certain conditions.
See the remarks section:
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.close.aspx
If you want to show a form again. Use the Hide() method instead of Close().
Hope that helps other searching souls.
And guys, don't stop searching at "I don't know why it works sometimes". That becomes buggy software with lots of defensive "I'll call this method again just in case" stuff. Not good.
Ok, hate to answer my own question, but this was driving me nuts, and it was one of the hardest bugs to reproduce I've ever seen.
On my form I'm overriding the OnFormLoad and OnFormClose methods, where I save/restore the form's Size, Location, and WindowState to/from the registry. I took this code out and it fixed the problem. The weird thing is, I put it back and the problem didn't come back.
I finally reproduced the problem: you have to let the form open fully, maximize it, and then close it so that the Maximized state is saved to the registry. Then when you open it again, it will set it to Maximized, and if it closes in the Load handler, it tries to access the Size/Location as it's closing. Apparently accessing these values in the OnFormClosing method causes the form to try to focus IF AND ONLY IF the form is maximized, which is illegal, since the form has been disposed.
So basically, you can't access Form display properties in the OnFormClosing method of a form, if that form is going to call Close from it's Load event.(Unless you check the Disposed prop first)
pretty specific piece of Winforms wisdom I know, but I'm writing it down anyway.
If you want to close a form as if the user pressed the cross in the upper right corner (usually means cancel), just add the following code.
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Close();
This also works in the form load function:
private void MyForm_Load (object sender, EventArgs e)
{
// do some initializations
if (!ContinueLoadingForm())
{
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Close();
return;
}
// continue loading the form
}
If you don't want the form to be visible for a short while, set the Visible property false (for example in the designer or constructor), and set it back to true when you are certain the program can continue loading.
In load event is not realy good idea close the form. Do it after the Activated event.
protected override void CreateHandle()
{
base.CreateHandle();
if (FormMustClose) //FormMustClose is a variable in the loadevent.
{
Close();
}
}
One possibility:
They may have a timer on this form, that is being initialized and enabled in their FormLoad event. The timer would need to be disabled and stopped as well, before the form was closed, if the timer is trying to access the form when it's fired.
I've seen forms before that do this...
It seems to me, without looking closely at it, that the cleanest way to accomplish what you want might be to make a custom form class deriving from Form, and override OnFormLoad(...) and/or Show() to check for your condition and cancel out early.
That said, I don't know why it would work sometimes and not other times.
Have you tried stepping into the .net code to see what line of code is being called when the exception is occuring? If you have VS 2008 you can do so by going to Tools --> Options --> Debugging and select the Enable .NET Framework Source Stepping. Be warned, this may take a while to download all of the necessary files, but this way you can step into the form.Show() and see exactly what is going on.
Ok, it turns out it's a little simpler and more generic than I thought, but still weird and obscure.
If you're saving/loading the form Size/Location/WindowState when the form loads/closes like we do, you have to make sure that the OnLoad method calls base.OnLoad first so that the Form Load event handler fires, and THEN set the properties. Not doing so will only cause a problem if the form calls Close from inside the Load method. You'll get an ObjectDisposedException on the Show call after the form closing event is done.
My head hurts.
Form.Shown() Is the trick too.
As I understand it, setting the DialogResult of the form will close the form - may have to be other than DialogResult.None. (i.e. you don't need to then call the Form.Close() method).
The issue is also in part that if elsewhere in code, you are accessing a property of the form or control within it, that may prevent the form from closing.
It may also be best if as has been suggested, you have a property e.g.
private bool _loadedOk = false;
in your form which you set in your initialisation code. In one of the later events after Form_Loaded, you then interrogate this and close the form if it's false.
Perhaps someone can suggest the best event to do this in??
If you want to close the form without flicker, the best way I found was override SetVisibleCore Method:
public partial class MyForm : Form
{
...
protected override void SetVisibleCore(bool value)
{
if (value && !IsHandleCreated && !ContinueLoadingForm())
{
base.SetVisibleCore(false);
this.Close();
return;
}
base.SetVisibleCore(value);
}
}
Then you can simply do:
...
var myForm = new MyForm();
myForm.Show();
...
The Form only will appear if ContinueLoadingForm() be true, this works with ShowDialog() and Application.Run() as well.
Expanding on RCMAN's answer in this thread (which got me 99% of the way to the finish line) ...
Here is the code I ended up using which also avoids the screen flicker:
Me.FormBorderStyle = FormBorderStyle.None
Me.Opacity = 0.01
Me.MinimumSize = New Size(1, 1)
Me.Size = Me.MinimumSize
Me.Location = New Point(1, 1)
BeginInvoke(New MethodInvoker(AddressOf Me.Close))
Additionally, to avoid the message "This program might not have run correctly" I applied a manifest change as described by mik here:
How to prevent "This program might not have installed correctly" messages on Vista