Frans Bouma's blog

Generator.CreateCoolTool();

Syndication

News

    Visit LLBLGen Pro's website

    Follow FransBouma on Twitter

    Add to Technorati Favorites

About me

Fun stuff I created

My work

Winforms Gotcha: Form.Close() doesn't always call Dispose()

I just ran into a weird issue. During profiling I saw that controls on a form which was already closed were still reacting to events. I checked whether the Dispose() routine of the particular Form was called, but it wasn't. However, the Dispose() routine of other forms was called after it was closed, as in: immediately.

The difference between the two situations was that if I used Form.ShowDialog(parentForm), a call to Close() on the particular form didn't call Dispose. Checking the Form.Close() documentation describes this behavior:

The two conditions when a form is not disposed on Close is when (1) it is part of a multiple-document interface (MDI) application, and the form is not visible; and (2) you have displayed the form using ShowDialog. In these cases, you will need to call Dispose manually to mark all of the form's controls for garbage collection.

I never knew that. It's easy to overlook, as opening a form with Show() will result in a call to Dispose when Close() is called. Not calling Dispose (or better: wrap the Form usage in a using block) will lead to a memory leak and worse: could lead to hard-to-find bugs because event handlers aren't cleaned up.

So just in case you use ShowDialog() or ShowDialog(form) to show modal dialogs in winforms, be aware that you've to call Dispose() yourself.

Published Friday, February 27, 2009 6:06 PM by FransBouma

Comments

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Friday, February 27, 2009 3:37 PM

I always use modal forms with "using" statement

andrex

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Saturday, February 28, 2009 9:42 AM

Great tip; not everyone reads the "fine print"

Thanks for posting.

SGWellens

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Saturday, February 28, 2009 12:52 PM

Aha, indeed. And if you think about it, it makes sense, specially for ShowDialog situation, since you usually want to retrieve information from it after the (user) closed it.

Miha Markic

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Monday, March 02, 2009 9:28 AM

If a Form doesn't get disposed during its Close(), it doesn't mean that it's never disposed.

Eventually, the GarbageCollector calls Dispose() on the Forms displayed with ShowDialog().

Other handles on the form may cause memory leak issues.

Filini

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Monday, March 02, 2009 10:36 AM

Digging a bit deeper in the issue, I've seen that the GarbageCollector calls Dispose() on those forms displayed with ShowDialog() which don't have any references (like public methods used as event handlers for other components).

This Dispose(), however, doesn't fire the Disposed event of the form.

Filini

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Monday, March 02, 2009 10:46 AM

@miha: good point, it is indeed useful, the only drawback is: it depends on the way the form is opened how Close() behaves. That's IMHO not really intuitive. What might have been better is a parameter to Close which would make the form become hidden instead of closed (or simply a Hide() method instead of a Close() method. )

@Filini: Dispose is called when the app is closed, as the Hidden forms are kept in memory, at least in my debugging sessions I saw that happening. Often this isn't something to worry about, but if the form holds event handlers, it might be worth it to look into this.

So indeed rule of thumb: use a using statement with modal dialogs opened with ShowDialog().

FransBouma

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Monday, March 02, 2009 11:33 AM

@FransBouma: I just set up a test app, and also used WinDbg to check the objects in memory. If you force the CG.Collect() you'll see that Dispose() is called (if the form doesn't have any reference) and the object is unloaded from memory.

The leak (object kept in memory) is caused by strong handles on the form, not by the missing Dispose(). Even if you use "using" or force a Dispose(), if the object is referenced it will not be removed from memory.

If you want, I can send you my test app.

Filini

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Monday, March 02, 2009 1:00 PM

@filini: I know. :) references to the form of course keep it in memory.

FransBouma

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Tuesday, March 03, 2009 3:09 AM

@frans: I agree that it isn't intuitive at all and probably a lot of devs doesn't realise this "feature". Even better rule of thumb: do dispose/using everything that implements IDisposable asap. :-)

Miha Markic

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Tuesday, March 03, 2009 4:39 AM

@Miha: sometimes, dispose/using is not enough. If you do something like this:

           using(Form3 f3 = new Form3())

           {

               this.Resize += new EventHandler(f3.FooEvent);

               f3.ShowDialog(this);

           }

Your Form3 is disposed, but it is never removed from memory, because its FooEvent() is used as an event handler for the main form.

After dealing with a big application with HUGE memory leaks problems, my rule of thumb is: do using/dispose for every form AND check all over the place for f*!@ng handlers/references :P

Filini

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Wednesday, March 04, 2009 10:32 AM

Thanks for bringing that up Frans!

I tend to go with Miha's advice ;-) plus trying to put as much as possible event handling into anonymous methodsm which won't save you completely though.

Regards,

André

André Knuth

# re: Winforms Gotcha: Form.Close() doesn't always call Dispose()@ Wednesday, March 04, 2009 5:02 PM

Frans, you said "Dispose is called when the app is closed, as the Hidden forms are kept in memory, at least in my debugging sessions I saw that happening"

I'm pretty sure I've read somewhere that the GC operates a bit differently in debug than it does in release. It keeps some objects around a bit longer so that you can examine them.

Richard Wright