2 July 2011

Closing all open forms in .NET

In your application you may have many hidden forms which you must close before exiting the application. It's because, hidden forms always stay in the memory until you close them. But, .net framework always takes care of disposing off all those objects when the application is terminated. But, the real problem comes when those hidden forms contain COM/ActiveX components. You all know, you must manually clean all unmanaged components. Now, if you forget to close such forms, it may lead to memory leaks.

It's very simple to close all such open forms. Application.OpenForms is the collection that contains all open forms. So, just loop through this collection and close one by one. Below is the code, where I am closing all open forms in main form's FormClosing event handler.


    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        foreach (Form openForm in Application.OpenForms)
        {
            openForm.Close();
        }
    }

On first look, this code looks to be fine. But, there are two potential problems with this code.
First,Application.OpenForms contains your main form because it is also open currently. So, when the loop executes, it will call Close() on MainForm, which in-turn will fire FormClosing event of MainForm. This will results in calling MainForm_FormClosing method recursively and infinitely which leads to deadlock. So, a StackOverFlowException will occur. To avoid this, you can add a check as below.


    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        foreach (Form openForm in Application.OpenForms)
        {
            if (openForm != this)
            {
                openForm.Close();
            }
        }
    }

simple, isn't it? Is it good to go now? No. Still there is a problem. To understand the problem, let us consider, we have 3 open forms. So, Application.OpenForms collection contains 3 items (say in following order)

   1. MainForm
   2. SubForm1
   3. SubForm2

Ok. The loop executes first time and skips the Close() since the condition fails. During second loop, it closes the SubForm1 and imporatantly SubForm1 is removed from Application.OpenForms collection. This means the collection is altered. Now, during 3rd loop, it tries to access the collection, an 'InvalidOperationException' is thrown saying "Collection was modified; enumeration operation may not execute". Hence, what you can do is instead of directly accessing Application.OpenForms, store all open forms in an array or a list. Then loop through the array/List and close the forms. So, below is the final code.


    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        List<Form> lstOpenForms = Application.OpenForms.OfType<Form>().ToList();

        foreach (Form openForm in lstOpenForms)
        {
            if (openForm != this)
            {
                openForm.Close();
            }
        }
    }


I hope this information was useful.

3 comments: