Section III. LOCATING LEAKS
So you have a leak? How do you find it?
1. The first suggestion, which can be used whether or not you have NT/XP, is to do a simple search:
a. Make a list of all the APIs you are using that create objects. You can use
the listing in the next section as a start, but that list is not all-inclusive. Therefore, hopefully, you researched your APIs so you know which ones are creating objects you should be destroying (
Good Coding Habits #8).
b. Start within any code page in your project (view the code). Press Ctrl+F and ensure the "Current Project" option button is selected. For each API name on your list, search your project for it. Once found, look for the line of code that is suppose to delete or destroy the object created by that API. If you can't find that line of code, then supply it and one leak is terminated. If the object created is global or public, look for the destruction statement in your Terminate or Unload event. If the object is locally declared in that routine, then the destruction statement should be in that routine. If the routine is returning a created object as a function parameter or return result, add that function/sub name to your list of APIs to search.
(1) Existing objects must be destroyed before they are re-created (
Good Coding Habits #5).
(2) If the object is selected into a DC, it must be unselected before it is destroyed. Failing to follow the "unselect-then destroy" order is a very common reason for many leaks (
Good Coding Habits #4)
c. Once you have identified all lines of code that create an object and have also identified the appropriate lines of code that delete or destroy those objects, test your project for leaks again. Rinse and repeat... Continue testing and correcting until all the leaks are gone.
2. For Windows NT and XP owners. The task manager can assist a bit more. Here are some tips. For Windows 9x owners, the following might offer a good place to start looking for leaks.
a. Is the leak in the hundreds as shown by
the GDI count in Task Manager? If so, look at any custom paint routines where you redraw a custom control or bitmap completely. Look inside loops that create pens and brushes or fonts. These are the most common places for such large leaks.
b. Is the leak only one, two or a few? If so, look at your global, public, or module-wide variables that are pointers to objects. Have you destroyed them in your Terminate or Unload event? This is the most common reason for very small leaks.
c. Let the project sit for a few seconds. Does the GDI count keep going up? If so, you have a timer that is calling a routine that is leaking or calling other leaky routines. Definitely a clue in the right direction to begin checking.
d. Move your mouse over any custom drawn controls without clicking. Does the count keep rising? If so, look at your mouse move event and the code that it contains or calls.
e. Is your project a custom drawn user control or object? If so, start a fresh project and add that control to your project. On a blank form, add only one of the controls. Test your project for leaks by following the steps in the previous section. Use that GDI leak count to determine where to start looking. If no leaks are present, change some of the user control's properties or settings that would create objects (i.e., font, picture, color properties) and try again. If you had a leak in the first project, you should have one in the new project. If not, then the leak may be in one of your other modules or classes in your first project.
2. If you cannot find the leak, you can try a more systematic approach to detection.
a. Try to place Exit Sub or Exit Function statements at the top of as many of your routines as possible and test a single routine at a time. If the routine doesn’t run, it can’t leak. When project is tested for leaks and none found, remove one of the remaining Exit statements and test the project again until all your added Exit statements are removed. If a leak is found, troubleshoot that routine to locate the leak before moving on to the next routine.
b. This isolation process may be impossible for many custom drawn control projects as they may have many routines that are dependent upon others, preventing isolating single routines. For such controls, it may be better to use Debug.Print statements in your routines and the Terminate event. Objects contained in individual routines should be easy enough to check visually since they should have a pair of creation and destruction API calls. Basically, for the non-local objects, you want to print whether or not global, public or module-wide variables are actually destroyed. Of course, immediately after the line of code that is suppose to destroy these objects, you should be resetting their variables to zero (
Good Coding Habits #3).
Basically add a Debug.Print “Created pen: “; [pen handle] for each GDI object you create and then Debug.Print “Destroyed pen “; [pen handle] for each GDI object you destroy. Change “pen” to the appropriate object type. When your program runs, you should have a pair of Created/Destroyed statements and if not, now you have a place to begin looking.
3. If all else fails… Pass your project off to a friend that has NT/XP and has a bit more experience in finding leaks. If the leaks are found, don't forget to thank them first, then ask them how they found it.