C++ Q&A

Window Destruction in the .NET Framework

Paul DiLascia

Code download available at: CQA0305.exe (120 KB)
Browse the Code Online

Q I have a question about window objects (Forms and Controls) based on your November 2002 column about nondeterministic destruction in the Microsoft® .NET Framework. With MFC, I can put cleanup code in my destructor because MFC deletes the window object automatically when the window is destroyed. What about with the .NET Framework? If a window is destroyed, does the .NET Framework immediately destroy the corresponding Form, or does it wait until a garbage collection to do this? If the .NET Framework doesn't free the Form object right away, how can I release my resources as soon as the window is destroyed? How does this relate to the Dispose pattern described in your column?

Q I have a question about window objects (Forms and Controls) based on your November 2002 column about nondeterministic destruction in the Microsoft® .NET Framework. With MFC, I can put cleanup code in my destructor because MFC deletes the window object automatically when the window is destroyed. What about with the .NET Framework? If a window is destroyed, does the .NET Framework immediately destroy the corresponding Form, or does it wait until a garbage collection to do this? If the .NET Framework doesn't free the Form object right away, how can I release my resources as soon as the window is destroyed? How does this relate to the Dispose pattern described in your column?

Zachary Morton

A Good questions! Other readers have asked me about window destruction and it's such an important topic, I decided to devote a whole column to it. But first let me point out that it's not quite accurate to say that MFC destroys window objects automatically, since it depends on what kind of window you're talking about. By default, views and frames destroy themselves automatically, whereas dialogs and controls (CButton, CListBox, and company) do not. In all cases, however, the place where MFC either does or does not delete the window object is in PostNcDestroy:

// view, frame delete themselves..
void CView::PostNcDestroy()
{
  delete this;
}
// ..but not most windows
void CWnd::PostNcDestroy()
{
}

A Good questions! Other readers have asked me about window destruction and it's such an important topic, I decided to devote a whole column to it. But first let me point out that it's not quite accurate to say that MFC destroys window objects automatically, since it depends on what kind of window you're talking about. By default, views and frames destroy themselves automatically, whereas dialogs and controls (CButton, CListBox, and company) do not. In all cases, however, the place where MFC either does or does not delete the window object is in PostNcDestroy:

// view, frame delete themselves..
void CView::PostNcDestroy()
{
  delete this;
}
// ..but not most windows
void CWnd::PostNcDestroy()
{
}

WM_NCDESTROY is the last message Windows sends as it destroys a window. MFC's default handler CWnd:OnNcDestroy does a bunch of stuff, then at the very end calls the virtual function PostNcDestroy, the last function MFC calls as your window bites the dust. It's a little weird for a member function to delete its own object instance, but that's exactly what CView and CFrameWnd do. Hey, it works!

Why don't dialogs and other windows delete themselves too? By convention, MFC creates frames and views dynamically on the heap using operator new, so they must be deleted. By contrast, you normally create dialog objects on the stack, which means they're destroyed when they go out of scope. No need to call delete. Other windows like CButton and CListBox live as members of their parent object—view or dialog—and get destroyed along with it. Whether the parent lives on the stack or heap, child objects die with their parent.

So CDialog objects are not destroyed as part of WM_NCDESTROY processing. But since you normally create your CDialog on the stack, call DoModal and then return, CDialog objects are destroyed very soon after the dialog itself—but only because the dialog goes out of scope, not because MFC deletes it. You can take advantage of this feature to implement dialogs that remember their state across invocations by making your dialog static:

void CMyApp::OnFooCommand()
{
  static CFooDialog d; // one  and  only instance
  d.DoModal();
}

Here, your app runs and reruns the same CFooDialog dialog object every time the user invokes the Foo command. The CFooDialog is created only once, the first time OnFooCommand is invoked, and is not destroyed until your program terminates. Whatever values you store in CFooDialog's member data are preserved from one invocation to the next.

So the upshot of this long MFC digression is: while MFC doesn't always destroy window objects automatically, the standard conventions ensure that the window object is destroyed more or less immediately after the window, so the proper place to free resources is still your window object's destructor.

What about the .NET Framework? When a form is destroyed, does the .NET Framework automatically free the corresponding Form or Control? If memory is your main concern, the answer is: who cares? Sooner or later, the common language runtime (CLR) will free your form. It doesn't matter when, so just let the .NET Framework do its thing. But what if your Form holds unmanaged resources like open file handles, Internet connections, direct satellite uplinks, or other precious goodies you want to release as soon as your window goes bye bye? Remember, it's not polite to hog the satellite connection. So how do you make sure your resources get released ASAP?

In the .NET Framework, any time you have resources to release, you should immediately think: dispose. Dispose is the place to free stuff. The question then becomes, does the Framework automatically call Dispose when your window is destroyed? The short answer is yes, more or less. If you call Form.Close, the Framework automatically Disposes your form. But Application.Exit doesn't automatically close your form. So if you want to close it (and Dispose, too), you must explicitly call Close before Application.Exit:

// in, eg, Form1 class
private void OnAppExit(...)
{
  this.Close(); // close myself
}

This has the side effect of calling Dispose and freeing your resources. But where exactly does the .NET Framework call Dispose? More important, how can you find out?

There are many ways to go spelunking in the .NET Framework. Two of my favorites (because they're so easy) are trace diagnostics and ILDASM. Trace statements are particularly useful for revealing which virtual functions get called when. Just implement a bunch of overrides that spit out a message before calling your base class, then see what happens. Figure 1 shows a generic Form app sprinkled with tracing. It helps to indent your diagnostics information so you can see where in the call stack each message comes. I wrote a little class, TraceScopeIndenter, to automate indenting (see Figure 2). TraceScopeIndenter's constructor increments the indent; Dispose decrements it. To use TraceScopeIndenter, add a using statement around your function or code block, like so:

   void SomeFn()
   {
     using (new TraceScopeIndenter()) {
       •••  // tracing here gets indented
     } // compiler calls 
       // TraceScopeIndenter.Dispose here
   }

Figure 2 TraceScopeIndenter

//////////////////
// Use this class to indent all diagnostics within a particular
// scope--function or curly braces:
//
// using (new TraceScopeIndenter()) {
//   // trace statements here will be indented.
// }
//
// You can use Trace.IndentSize to set the number of spaces for each 
// indent. 
//
public class TraceScopeIndenter : IDisposable
{
    private bool disposed = false;

    // ctor: increment indent level
    public TraceScopeIndenter() {
        Trace.IndentLevel++;
    }

    // disposer: decrement
    public void Dispose() 
    {
        if (!disposed) {
            Trace.IndentLevel--;
            disposed=true;
        }
    }
}

Figure 1 Generic Form with Tracing

///////////////////////////////////////////////////////////////
// MSDN Magazine — May 2003
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio .NET on Windows XP. Tab size=3.
//
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using TraceWin;

////////////////////////////////////////////////////////////////
// This sample app does nothing more than reveal which virtual functions
// get called when a window is destroyed. If you want your Dispose
// method to get called when your form is destroyed, you must
// explicitly call Form.Close before exiting your app.
//
namespace FormApp
{
    //////////////////
    // Typical control, to see when it gets destroyed.
    //
    public class MyButton : Button
    {
        protected override void Dispose( bool disposing )
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine(String.Format("MyButton.Dispose({0})",
                    disposing));
            }
        }
        protected override void OnHandleDestroyed(EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("MyButton.OnHandleDestroyed");
                base.OnHandleDestroyed(e);
            }
        }
        ~MyButton()
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("MyButton.dtor");
            }
        }
    }

    // Typical form
    public class Form1 : System.Windows.Forms.Form
    {
        private MyButton button1;
        private bool disposed = false;
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            Trace.WriteLine("Initializing...");
            InitializeComponent();
        }

        ////////////////
        // Basic Dispose method frees all resources. Normally this will
        // be called eventually by the garbage collector; if you want it
        // called immediately when your window is destroyed, you must
        // close your window by calling Form.Close
        //
        protected override void Dispose( bool disposing )
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine(String.Format("Form1.Dispose({0}), 
                                disposed={1}",
                    disposing,disposed));
                if (!disposed) {
                    if( disposing ) {
                        Trace.WriteLine("Form1 disposing");
                        if (components != null) {
                            components.Dispose();
                        }
                    }
                    disposed = true;
                    Trace.WriteLine("Form1 calling base.Dispose...");
                    base.Dispose(disposing); // calls Dispose for child 
                                             // Controls
                    Trace.WriteLine("Form1 base.Dispose returned.");
                }
            }
        }

        #region Windows Form Designer generated code
        private void InitializeComponent()
        {
            this.button1 = new MyButton();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(120, 176);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(80, 32);
            this.button1.TabIndex = 0;
            this.button1.Text = "&OK";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(208, 213);
            this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                   this.button1});
            this.Name = "Form1";
            this.Text = "Hello";
            this.ResumeLayout(false);

        }
        #endregion

        [STAThread]
        static void Main() 
        {
            Debug.Listeners.Add(new TraceWinListener());
            Trace.IndentSize=1;
            Application.Run(new Form1());
            Trace.WriteLine("Form1 Calling Application.Exit...");
            Application.Exit();
            Trace.WriteLine("Form1 Application.Exit returned...");
        }

        // user clicked "OK" button
        private void button1_Click(object sender, System.EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1 OK clicked, calling this.Close...");
                this.Close();
            }
        }

        // This virtual fn is called when the window handle (HWND) is 
        // destroyed.
        protected override void OnHandleDestroyed(EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.OnHandleDestroyed");
                base.OnHandleDestroyed(e);
            }
        }

        // This virtual fn is called when the window is closed (WM_CLOSE).
        // The base class implementation raises the Closed event. For
        // derived classes, you can handle the Closed event by overriding
        // this method instead of attaching a delegate. This is the
        // preferred way.
        //
        protected override void OnClosed(EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.OnClosed");
                base.OnClosed(e);
            }
        }

        // This virtual fn is called before the window is closed. Same
        // remarks apply as for OnClosed. You can set
        // CancelEventArgs.Cancel=true to cancel closing the window.
        //
        protected override void OnClosing(CancelEventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.OnClosing");
                base.OnClosing(e);
            }
        }

        // This fn destroys the window handle (HWND). It calls
        // NativeWindow.DestroyHandle, which calls ::DestroyWindow.
        // This function is called from Control.Dispose. You must call
        // the base class here to actually destroy the handle (calls
        // ::DestroyWindow).
        //
        protected override void DestroyHandle()
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.DestroyHandle");
                base.DestroyHandle();
            }
        }

        protected override void WndProc(ref Message m)
        {
            using (new TraceScopeIndenter()) {
                // uncomment line below if you want to see all messages
                // Trace.WriteLine(String.Format("DefWndProc: {0}",m.Msg));
                if (m.Msg==2) {
                    // never called
                    Trace.WriteLine("Form1 got WM_DESTROY");
                } else if (m.Msg==16) {
                    // never called
                    Trace.WriteLine("Form1 got WM_CLOSE");
                } else if (m.Msg==130) {
                    // never called
                    Trace.WriteLine("Form1 got WM_NCDESTROY");
                }
                base.WndProc(ref m); // important!
            }
        }

        // This is the destructor (Finalize method). It should never get
        // called because Disposing an object removes it from the
        // Finalization queue. The CLR calls Finalize only if your object
        // fails to get Disposed.
        ~Form1()
        {
            Trace.WriteLine("Form1.dtor");
        }

    }
}

Figure 3 Before TraceScopeIndenter

Figure 3** Before TraceScopeIndenter **

Figure 3 and Figure 4 show a typical trace dump before and after TraceScopeIndenter. A little indenting sure goes a long way to reveal what's going on! Once you have tracing, you can try various things and see what happens. For example, Figure 5 shows what happens if you don't call Form.Close before exiting your app: the .NET Framework does eventually dispose your form, but only after your application exits.

Figure 4 After TraceScopeIndenter

Figure 4** After TraceScopeIndenter **

My other favorite spelunking tool is ILDASM. One of the best things about the .NET Framework is that it comes with full source! Well, not exactly. You don't get to see C# code with witty comments, but you can disassemble any .NET Framework class into Microsoft Intermediate Language (MSIL) as long as you know which DLL it lives in.

Figure 5 TraceWin

Figure 5** TraceWin **

For example, to see what Form.Close does, open a console window, navigate to your framework directory, and type:

ildasm System.Windows.Forms.dll

You'll get a dialog like the one shown in Figure 6. You can expand the classes and double-click on any function—er, I mean method—to disassemble it. The code may look hieroglyphic, but you don't have to be an Egyptologist to get the gist. The highlighted lines in Figure 7 show that Form.Close simply calls SendMessage with uMsg = 16, which is WM_CLOSE. Most readers will be able to glean what's happening without a road map, but if you're curious, there's an ILDASM tutorial that comes with the MSDN® Library and a reference manual in \FrameworkSDK\Tool Developers Guide\docs\Partition III CIL.doc in your Visual Studio® .NET installation. Fellow columnist John Robbins' excellent May 2001 Bugslayer column provides a crib sheet for common MSIL instructions.

Figure 7 Form.Close in ILDASM

Figure 6 ILDASM

Figure 6** ILDASM **

Of course, the most surefire way to find out who calls your form's Dispose method is to add a breakpoint, run your application in the debugger, and examine the call stack when it croaks. Figure 8 shows the resulting screen.

Figure 8 Call Stack

Figure 8** Call Stack **

With the combined efforts of trace diagnostics, ILDASM and the debugger, I can now state definitively what happens when your form is closed, starting when the user presses the OK (exit) button. First, the Framework calls the button's click handler, button1_Click. This in turn calls Form.Close. Form.Close sends a WM_CLOSE message to the form (see Figure 7). Form.WndProc catches the message and dispatches to a private method, Form.WmClose. WmClose does a bunch of stuff—including calling OnClosing and OnClosed to signal the Closing and Closed events—before finally calling Component.Dispose:

// Form.WmClose.
.method private void WmClose(...)
{
  ••• // lots of gnarly MSIL
  IL_0125:  ldarg.0 // "this" pointer
  IL_0126:  call instance void 
    [System]System.ComponentModel.Component::Dispose()
  IL_012b:  ret
}

When I saw this I immediately thought, "Aha!" But just as quickly I was disturbed because the call to Component.Dispose is a direct call, not a callvirt, which means that control goes directly to System.ComponentModel.Component.Dispose, not my virtual override. But if you examine Component.Dispose (the [System] preceding the function name tells you it's in System.dll, not System.Windows.Forms.dll), you'll see that it calls the second, overloaded Dispose method, the one that takes a bool argument. And this time the call is virtual:

  // ComponentModel.Dispose
  .method public virtual void Dispose()
  {
    •••
    IL_0001:  ldc.i4.1 // True
    IL_0002:  callvirt instance void 
     System.ComponentModel.Component::Dispose(bool) 
    •••
  } 

Whereupon control finally arrives at my Form1.Dispose—Whew!

That's probably way more than you ever wanted to know about window destruction in the .NET Framework, but at least now you have the wherewithal to go about investigating these mysteries yourself. If you do, you'll be richer for the experience. I gleaned several useful tidbits along the way to completing this exercise. First, there's no need to override OnClosed to Dispose of your resources, which is something I have seen many programmers do. All you have to do is free your resources in Dispose and remember to call Close. If you don't, overriding OnClosed has no effect because it won't be called.

Also of interest is Form.WndProc, the method that catches all Windows messages (WM_XXX). It's a giant switch dispatch to private methods WmActivate, WmClose, WmCreate, and so on. Ditto for other window objects like Control and ListView. So if you see a method WmWhatever, chances are good that it's the handler for WM_WHATEVER.

When I first started investigating, I guessed the form might be disposed in Form.OnClosed. But the methods OnClosing and OnClosed don't actually do anything except raise the corresponding Closing and Closed events. This is a general principle in Windows.Forms. Many events have a virtual method that does nothing but raise the event. This means that derived classes can handle the event by overriding the method instead of attaching a delegate. According to the official .NET Framework documentation, "this is the preferred technique for handling the event in a derived class." Just make sure that you call the base class method at the end to ensure the event gets raised for any other (nonderived) objects that may be listening.

Finally, as you go looking for functions to disassemble and learn from, you may discover that a particular class you're looking for is not in the location where you expect it to be. For example, System.ComponentModel.Component is in System.dll, not System.Win dows.Forms.dll. The tipoff is when you see a name in square brackets preceding the method name—for example [System] or [mscorlib]. This tells you which DLL to look in.

I uncovered one more mystery while exploring window destruction in the .NET Framework: I added a trace diagnostic to my form's destructor, but it never got called. This feels highly irregular to a longtime C++ programmer. If a destructor fails to get called you immediately think memory leak. But in the .NET Framework, the Finalize method (destructor in C#) is really only a backup in case an object fails to get Disposed. If an object is Disposed, there's no need to Finalize it—and, in fact, the last thing Component.Dispose does is call System.GC::SuppressFinalize on the object disposed to remove it from the Finalization queue. If you implement your own Dispose function for a class not derived from Component or any other class that implements Dispose, you should make sure to SuppressFinalize as well. Figure 9 shows the standard Dispose pattern. Happy programming!

Figure 9 Dispose Pattern

///////////////////////////////////////////////////////////////////
// Dispose design pattern for base and derived classes

///////////////////
// Base class. Note that many .NET Framework classes already implement 
// this stuff so all you have to do is implement the derived piece.
//
public class Base: IDisposable
{
   // implement IDisposable.
   public void Dispose() 
   {
        Dispose(true);
      GC.SuppressFinalize(this); 
   }

   protected virtual void Dispose(bool disposing) 
   {
      if (disposing) {
         // free managed objects
      }
      // free unmanaged objects
      // set large fields to null (de-reference)
   }

   // Use C# destructor syntax for Finalize method code.
   ~Base()
   {
      Dispose (false); // simply call Dispose(false)
   }
   
//////////////////
// Derived class. The derived class does not have a Finalize method (C#
// dtor) or a Dispose method with no parameters because it inherits them
// from the base class.
//
public class Derived: Base
{   
   protected override void Dispose(bool disposing) 
   {
      if (disposing) {
         // free managed resources.
      }
      // Free unmanaged resources.
      // Set large fields to null.
      // Call base class Dispose.
      base.Dispose(disposing);
   }
}

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLasciais a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at https://www.dilascia.com.