Finding leaks

This article describes the various methods for tracking down GDI and User handle leaks.  Unfortunately in Win2k/XP you cannot tell which kind of handles are out (windows 9x used to distinguish) - however I haven't usually needed to know this kind of information.

If you open up task manager and add in the GDI/User handle column - the article discusses this -  you should be able to observe when the handles spike in your application.  Once you've narrowed down roughly where you are observing the spike, say hovering over a button keeps growing the number of GDI handles by 4 every time.... you can add in calls to GetGUIResources to divide and conquer the problem.  Usually you'll find something IDisposable that wasnt disposed - like a pen or a brush or a string format object.

I thought I'd share the class I use in some of my tests to ensure that my painting code doesn't leak.  I run the painting code through once to make sure all the static SystemPens/Brushes are all cached in, then rerun it wrapping the painting code in a GDICounter object.

private bool runonce = false;
protected override OnPaint(PaintEventArgs e) {

   if (runonce) {  
      using (new GDICounter()) { // the constructor snaps the current count of GDI objects by calling GetGUIResources
          base.OnPaint(e);
      } // the dispose method is called here and compares the current count to the last known number.
   }

   runonce = true;

}

namespace FindLeak
{
    using System;
    using System.Runtime.InteropServices;
    using System.Diagnostics;

    public class GDICounter : IDisposable
    {
        private const int GR_GDIOBJECTS = 0, GR_USEROBJECTS = 1;
        [DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern int GetGuiResources(IntPtr hProcess, int uiFlags);
        private int intialHandleCount;

        public GDICounter() {
            intialHandleCount = 0;

            intialHandleCount = GetGDIObjects();
        }

        private int GetGDIObjects() {
            IntPtr processHandle = System.Diagnostics.Process.GetCurrentProcess().Handle;

            if (processHandle != IntPtr.Zero) {
                return (int)GetGuiResources(processHandle, GR_GDIOBJECTS);
            }

            return 0;
        }

        public void Dispose() {
            int currentHandleCount = GetGDIObjects();
            if (intialHandleCount != currentHandleCount) {
                int change = currentHandleCount - intialHandleCount;
                if (change > 0) {
                    Debug.Fail("Handle count changed by: " + change.ToString() + new StackTrace().ToString());
                }
            }
        }
    }

}

Once you've established there is a leak, you can divide and conquer by sprinkling in more calls to GetGUIResources until you've found your problem.

Why is a leak bad?  Wont the garbage collector get it? 

This is exactly what you dont want to have happen.  System.Windows.Forms/System.Drawing keeps a close eye on the number of handles out, to make sure that carefree usage of Pens/Brushes/Controls etc is cleaned up before the operating system runs out of resources.  Under the covers there are threshholds of handle counts that are appropriate for each handle type - if this is exceeded, a garbage collection is performed to clear out the controls/pens/brushes that have yet to be finalized.  If the app is creating lots of pens and brushes and not disposing them, this collection could happen at a time that's not convenient - say in the midst of a paint.  

This can all be prevented by deterministically cleaning up these resources (via the Dispose method). More details on how, when, where, and why you should use dispose here.