tags:

views:

621

answers:

6

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:

  1. Control.Show() is called
  2. my form is launched
  3. the OnFormLoad method is called
  4. the FormLoad event handler is called, inside of which I call this.Close()
  5. the OnFormClosing method is called
  6. the FormClosing event handler is called
  7. 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, and no 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?

A: 

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.

mquander
actually, that is the case here. I didn't mention it b/c all of my forms are that way. The base form overrides OnFormLoad and OnFormClose, but it just does registry stuff in those methods. The call sequence is the same in all forms.
LoveMeSomeCode
+1  A: 

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...

Reed Copsey
good thought, but no timers on this form. several user controls though, but I checked to make sure all of their Dispose methods are being called.
LoveMeSomeCode
+1  A: 

In load event is not realy good idea close the form. Do it after the Activated event.

TcKs
A: 

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.

Ross Goddard
no, I wish. I'm using VS 2005. I checked reflector though, and it's just like the call stack says: right near the end of the Control.Show it calls FocusActiveControlInternal, which calls the Handle getter, which throws the exception if it's disposed.
LoveMeSomeCode
A: 

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.

LoveMeSomeCode
A: 

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.

LoveMeSomeCode