Resource Leaks: Detecting, Locating, and Repairing Your Leaky GDI Code

Christophe Nasarre
This article assumes you�re familiar with GDI
Level of Difficulty     1   2   3 
Download the code for this article: Leaks.exe (106KB)
Browse the code for this article at Code Center: GDIDEBUGTOOLS
SUMMARY Leaks are possible, even in robust Windows-based applications. As bugs go, leaks are some of the most difficult to find, especially when they occur in graphics device interface (GDI) objects and menus. While free and third-party tools exist to detect such leaks, it is usually difficult to make the connection between the numeric handle value returned by the tool, and a bitmap or menu in your program. This can limit the usefulness of these tools.
      Custom tools can be built that detect, track down, and eradicate GDI and menu leaks. Here, three such tools are built using well-known and documented APIs.
Leaks are something you have learned to fear when developing Windows®-based applications using Visual C++®. Generally speaking, leaks result when you forget to release a system resource. Even if the documentation states that Windows takes care of releasing the resource used by a process when it exits, this is not enough for server applications that must be extremely reliable. Even a little leak can bring down the system if it occurs many times. There are usually three steps in leak eradication:
  1. Detect a leak
  2. Find the leaking resource
  3. Decide where and when the resource should be released in the source code
      This article introduces three tools I wrote that will help you detect and find the leaking resource. First, for Windows 2000, I present a tool to compare snapshots of graphics device interface (GDI) consumption per process. Then, for Windows 98, I provide tools that show a graphical display of the GDI and menu objects that are not released. Note that the tools in this article use techniques that are undocumented and are not supported by Microsoft.

Tool Options

      The most common system resource used in app development is memory, so it is not surprising that the first Bugslayer column published in the October 1997 issue of MSJ was an enhanced memory-allocation-tracking framework. Both the C runtime library and MFC provide a set of functions that help developers find such leaks. Matt Pietrek also provides code to help you understand the memory consumption of your application at a lower level than the C runtime library and MFC in Under the Hood in the November 1999 issue of MSJ.
      For system resources other than memory, it is hard to find helper classes or functions useful for detecting and locating leaks. Very often, separate tools are used to detect leaks. The Windows Task Manager provides only a raw view of resource consumption, as you will see later. A number of third-party tools, including Rational's Purify tool and Compuware Numega's BoundsChecker tool, provide users with a detailed summary of possible leaks at the end of the application's life, in addition to providing API parameter-checking and other rich error-finding features.
      This is fine, but for servers, services, or controls that are hosted by other applications, the needs may be different and the use of third-party tools is often trickier or impossible. It would be nice if Windows gave some hooks into its different resource allocation mechanisms. For example, the COM runtime allows you to register an implementation of the IMallocSpy interface whose methods are executed for every allocation, deallocation, and reallocation of COM memory such as those made through CoTaskMemAlloc calls. Don Box gives his own implementation of IMallocSpy in the October 1995 issue of MSJ that you should check out if you are writing complex ActiveX or simple COM objects.

Managing GDI Resources

      So, what about GDI Resources? Each GDI resource type is allocated through a specific API, but when it is time to release them, usually only one function is needed: DeleteObject. Figure 1 shows a list of object types and their corresponding allocation and deallocation functions. The GDI objects returned by GetStockObject and other GetXxx functions are not supposed to be explicitly released by an application using DeleteObject.
      Not surprisingly, the LoadImage API is implemented in USER32.DLL rather than in GDI32.DLL because it deals with resource (.rc and .res) files. This API function is also used to load icons and cursors. Figure 2 shows the allocation and deallocation functions for icons and cursors.
      Unlike the memory that is accessed through a C/C++ pointer, an allocated GDI object is represented by a handle. Under Windows NT® and Windows 2000, this handle is a 32-bit magic cookie that hides the internal structure of the resource managed by Windows. I'll discuss GDI object handles more in the section on finding the leaking resources.

Using MFC for GDI Programming

      MFC contains many classes that wrap GDI objects. Each inherits from the CGdiObject class (except for DC classes, as you will see later in this section), which offers an object-oriented view instead of calling raw GDI functions. Another advantage of these classes lies in the CGdiObject destructor found in AFXWIN1.H:
_AFXWIN_INLINE CGdiObject::~CGdiObject()
   { DeleteObject(); }
There is nothing special here except the call to CGdiObject::DeleteObject.
BOOL CGdiObject::DeleteObject()
{
   if (m_hObject == NULL)
      return FALSE;
   return ::DeleteObject(Detach());
}
The source code for this is in WINGDI.CPP.
      Each time a GDI object is wrapped by an MFC class, its handle is stored in the corresponding m_hObject member and a cross-reference is saved in an MFC internal map. CGdiObject::DeleteObject simply removes the handle from this map via the Detach function before calling the GDI DeleteObject function. Therefore, MFC wrapper classes let you forget about DeleteObject calls because DeleteObject is executed each time the object gets deleted or goes out of scope, but the need to call SelectObject twice remains. Since there is no need to explicitly call DeleteObject when you don't need a GDI object anymore, there are fewer opportunities to create leaks.
      For DC classes, MFC provides multiple classes that can really help, if you use the right one! CPaintDC should only be used in a WM_PAINT message handler since it handles BeginPaint and EndPaint for you in its constructor and destructor, respectively. The CClientDC class does the same for GetDC and ReleaseDC in order to let you write on the client area of a window. To access the entire window, even the nonclient area, CWindowDC is the one to choose, since GetWindowDC is called in its constructor and ReleaseDC in its destructor. I'm not referring here to CPreviewDC and CMetafileDC, as they don't really follow the usual constructor and destructor schema. In addition, MFC does not provide the same anti-leak mechanisms that are available for memory: no snapshot, no comparison, and no final dump.
      Now that you know that it is possible to forget to release GDI objects, let's find a way to find the leaks.

Step 1: Detecting a GDI Leak Under Windows 2000

      A good solution for detecting GDI leaks is to use dedicated tools such as the Purify or BoundsChecker tools I mentioned earlier. The main drawback of these tools is the same as the final MFC dump for memory leaksâ€"you don't know about the leaks until the application ends.
      It would be nice to be able to take a snapshot of current GDI consumption, perform an action that might leak, and make a comparison with the current state. Under Windows 2000 you can use the Task Manager to do this kind of comparison, thanks to a new column that displays the count of GDI objects used by a process (see Figure 3).

Figure 3 Columns for GDI Use
Figure 3 Columns for GDI Use

      This is nice, but you can't get detailed information about the type of the GDI object being used. Moreover, you have to use a pen and a sheet of paper to make the comparison by hand. Since this could be the basis of a handy tool, let's try to find out how Task Manager retrieves this value. Launch DEPENDS.EXE, which comes with Visual C++, and have it load TASKMGR.EXE.
      Surprisingly, the GetGuiResources function from USER32.DLL returns either the count of USER or GDI objects used by a given process. This function is exported by USER32.DLL (as you can see in Figure 4), but the GDI handles are created and managed by functions from GDI32.DLL because both GDI and USER were moved to the kernel (WIN32K.SYS) starting with Windows NT 4.0. GetGuiResources is mentioned by Matt Pietrek in his article "A Programmer's Perspective on New System DLL Features in Windows NT 5.0, Part I" in the November 1997 issue of MSJ when he describes the new APIs available with Windows 2000. The bad news is that it is only supported on Windows 2000 and the returned count is the sum of all GDI objects, so there is no way to get a per-object type count. Nevertheless, this is a good starting point and it is the one I used for my GDICount tool (see Figure 5), which monitors the GDI usage on a per-process basis.

Figure 5 GDICount
Figure 5 GDICount

      Let's look at the implementation of GDICount. Since the UI only calls GetGuiResources, the implementation is not complicated. The first of its two parameters is a process handle and the second is either GR_GDIOBJECTS for GDI consumption or GR_USEROBJECTS for USER objects count. Once a process ID is entered by the user and stored in the m_PID class member, a corresponding handle is retrieved with the following call:
OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, m_PID)
The first parameter specifies the access rights needed to call GetGuiResources. For the second parameter, FALSE means that you don't want the handle to be inherited (even if, in this case, nothing changes if you pass TRUE).
      The value of the GDI consumption is stored in the m_GDICount member when the Take Snapshot button is pressed. This value is then compared to the current GDI objects count when the Compare button is pressed, and the difference is displayed. Since this article is focused on GDI objects, the USER consumption is not traced. To learn more about how I implemented features that you can reuse in your own code, see the sidebar "Implementation Tips and Tricks."
      The GDICount tool just takes a snapshot and compares it to a previous count of resources. This is the minimum requirement for completion of the first step in the leak eradication processâ€"detecting the leak. The next logical step is to get the count-per-object type in order to limit the scope of the search. In fact, there is a way to get a detailed view of allocated GDI objects per process, but it is not documented by Microsoft. In the February 1999 issue of Windows Developer's Journal, Chris Branch provided source code for a tool that lists the count of the different types of GDI objects allocated for a given process under Windows NT 4.0. Unfortunately, the undocumented services he used are not available under Windows 2000.

Step 2: Finding the Leaking Resource in Windows 9x

      Now that you have a tool that monitors the GDI usage, it is possible to detect whether an application is leaking or not. The next step is to obtain the most detailed description possible of the GDI object that has not been released. Since Windows NT 4.0, the GDI (and USER) code has been moved from the user space to the kernel realm. Now GDI objects and data structures are in a space where only device drivers are allowed to roam. However, if you have a machine running Windows 9x available, I'll show you a documented and easy way to see, with your own eyes, the GDI objects used by the running applications. Yes, you've read correctly: it's possible to write a tool that displays the GDI objects.
      Let's start at the beginning. As Matt Pietrek explains in his book Windows 95 System Programming Secrets, each GDI object handle is a 16-bit cookie, even for 32-bit applications under Windows 9x. The cookie value is used by the system to get access to the real data structure in either a 16-bit or 32-bit heap dedicated to GDI objects. And such a handle is also valid system-wide. This means that its value can be used by any process to call GDI functions. What great news! Instead of peeking into the different GDI heaps (both 16-bit and 32-bit), it is possible to use one of the documented functions provided by GDI, GetObjectType, which takes a GDI object handle and returns its type among the values from WINGDI.H, which are shown in Figure 6.
      The engine of my GDIUsage tool is based on a simple loop from zero to the max 16-bit value (0xFFFF), where the GetObjectType function is called in order to determine whether a value is a GDI object and what type of object it corresponds to.
      The implementation of the GetObjectType function is odd: for some random values, it returns one GDI object type even if there isn't an object that corresponds to that value. The major drawback of such behavior is the nature of API function failures when called with these pseudo (but invalid) handles. The failure takes the form of a GPF that can't be handled by the application even with a try/catch! (You'll see another example of GetObjectType misbehavior later in this article.) Therefore, you need to enforce the return value of the GetObjectType function. In addition to an in-depth explanation of the GDI data structures details, Matt Pietrek, in his previously mentioned book, provides the pseudocode for a Windows 95 version of GetObjectType where you learn that a GDI handle follows a few rules:
  1. The value of a handle to a metafile or enhanced metafile is supposed to be a memory selector. Unfortunately, there is no documented or easy way to verify that a value is a selector to a metafile.
  2. Fonts and regions are stored in a specific 32-bit heap. Therefore, their handles are a multiple of four.
  3. The other objects (bitmaps, brushes, palettes, pens, and DCs) are stored in a 16-bit heap. Such handles are moveable, and therefore their values end in 2, 6, A, or E.
      The GDIHandleSeemsValid function in the GDIUsage tool takes a handle value and returns TRUE whether the second and third rules are verified or FALSE. But this checking is not sufficient to avoid GPFs. Unfortunately, some invalid values are not filtered by these rules. Usually they appear to be valid brushes, but they trigger a GPF when passed as a parameter to the FillRect function. On different machines where my tool has been tested, this happens for values higher than 0x6000 and this specific test has been added to the GDIHandleSeemsValid function. If some brush values make the tool crash on your machine, feel free to modify the checking code.
      After the loop ends in my GDIUsage tool, you have a snapshot of each and every GDI object used by all applications, as shown in Figure 7. At first glance, this appears to be less interesting than what the GDICount tool provides. This is because you don't know which process is using which GDI object. However, this doesn't really matter since the snapshot comparison is what's important, and if the leaking application is the only one running, you will see its objects, except for some regions used by Windows during repainting.

Figure 7 Starting GDIUsage
Figure 7 Starting GDIUsage

      Three C++ classes are responsible for the snapshot and comparison algorithms. CGdiObj wraps a GDI object handle and its type in two members: m_Handle and m_Type. One CGdiObj is instantiated for each valid handle obtained in the loop.
      A snapshot is stored by the CGdiResources class. Its Refresh method implements the loop engine to fill up a handle map:
CTypedPtrMap<CMapPtrToPtr, DWORD, CGdiObj*> m_HandleMap;
      This associates a CGdiObj object with a GDI handle and also updates a counter for each type in a dedicated array member:
DWORD m_CountByType[NB_GDI_TYPE];   
      It is possible to iterate through the snapshot using the GetFirst and GetNext methods.
      For the comparison, the CResourcesCompare class derives from CGdiResources, but the ComputeCompare function replaces the Refresh function and takes two CGdiResources snapshots as parameters. The former is the new GDI state and the latter contains a reference snapshot. In addition to m_CountByType, there are two new arrays:
DWORD m_OutCountByType[NB_GDI_TYPE];
DWORD m_NewCountByType[NB_GDI_TYPE];
The members used to count the objects are shown in Figure 8.
      The m_HandleMap member is filled up with the objects that were not in the reference, but which are present in the new snapshot (counted by m_NewCountByType).

Figure 9 Content of a Memory DC
Figure 9 Content of a Memory DC

      The next step is to display the GDI objects stored in CGdiResources, as shown in Figure 9. This is the role of CGdiResourcesDlg, which is initialized with a pointer to the CGdiResources list to display. The list in the left pane of Figure 9 presents the GDI objects sorted by type and handle. When an item is selected, the right side of the dialog displays a more detailed description of the object. Figure 10 shows the description for each GDI object type.
      In addition to showing the GDI objects, it is possible to get another type of description using GetObject. The GetObject function takes a handle, a buffer, and the buffer size as parameters. I'm only using this function to validate the GDI object handles before showing them, not to get additional information, since all the corresponding objects (bitmap, pen, extpen, brush, and font) are represented graphically in the dialog. The exception is a palette. The count of palette entries is retrieved using GetObject. You could also call the GetObject function in GDIHandleSeemsValid to enforce the object type returned by GetObjectType. Even if GetObjectType has returned a GDI object type for a particular handle, this handle may be invalid. In that particular case, a call to GetObject with the same handle will fail. The code to display the object is stricter than the engine loop because some objects might have been deleted between the time the snapshot was taken and the time the object itself is displayed.
      As an example, take a snapshot before displaying the About dialog in Visual Studio®. Figure 11 shows what you get after clicking the Compare button. The right side presents the comparison results.

Figure 11 Comparison Results
Figure 11 Comparison Results

      If you click on the Details button below the Compare button, you get a graphical display of each new object (see Figure 12). The selected GDI object is displayed on the right side of the dialog. The value of this tool is not the information it provides about a GDI object, but the fact that you can see the object.

Figure 12 Graphically Displaying Each Object
Figure 12 Graphically Displaying Each Object

      Although the documentation states that GetObjectType checks that an object handle refers to a GDI object, this function can be called with a random number, which will freeze some machines running Windows 95. I've found no way to avoid this behavior, even if you execute the application under a debugger.
      As you have already seen, another problem with the GetObjectType function is its unreliability. Sometimes it returns a certain GDI type for an object that is already deleted. Let's take a look at the sample code in Figure 13.
      If you execute the code in Figure 13, you get this output:
First-chance exception in TestGDI.exe (GDI32.DLL): 0xC0000005: 
Access Violation.
0x73e is a OBJ_BRUSH
â€"> valid brush
First-chance exception in TestGDI.exe (GDI32.DLL): 0xC0000005: 
Access Violation.
After deletion: 0x73e is a OBJ_BRUSH
â€"> valid brush
The first chance exceptions probably mean that GDI protects the code to get the object type by using try/catch. As you can see, the brush is detected as valid after being deleted, and even the GetObject call is successful! One reason might be that the solid brush is in fact a stock object, since its handle has the same value as returned by a GetStockObject(BLACK_BRUSH) call. But this does not seem to be the only reason. Other pure color brushes that are different from the stock objects are cached, and if you create the same brush after deleting it, you get the same handle.
      This could be due to the fact that, behind the scenes, when an application requests a pure color brush, Windows not only creates it, but it also stores its handle in a dedicated cache. In this case, even if the application calls DeleteObject on the handle it receives for the brush, Windows doesn't really delete it. Therefore, if the application requests a brush of the same color again, the same handle is returned. The use of GdiSetBatchLimit(1) or GdiFlush doesn't affect this behavior. Therefore, it is not possible to use GetObjectType in validation code to know if an object still exists or notâ€"you just have to hope that Windows automatically cleans up these cached objects.
      Is GetObjectType the only function in the Win32® API that could be used to identify system resource handles? The answer is no. The USER32.DLL exports a function named IsMenu that takes a menu handle as a unique parameter and returns whether it is valid or not. My MenuUsage tool is based on this function. The tool interface is shown in Figure 14.

Figure 14 MenuUsage UI
Figure 14 MenuUsage UI

      The engine follows the same procedure as the GDIUsage tool:
  1. Enumerate each possible value (from 0 to 0xFFFF) and store it if IsMenu returns TRUE.
  2. List the menus created since the last snapshot.
  3. Provide a visible description of a menu handle when needed.
      The last item is easily implemented using the TrackPopupMenu function, which takes the following parameters:
  • Handle of the menu to display.
  • Various flags indicating whether you want to know which item has been selected or how the menu will be aligned based on the mouse cursor position.
  • Horizontal and vertical position where the menu should appear.
  • A reserved value (must be 0).
  • Handle of the window that may receive notification messages (not used here).
  • Pointer to a rectangle (ignored).
      The menu handle that the TrackPopupMenu function displays is supposed to be a submenu. If the menu is a menu bar, such as a top-level menu of a window, this function provides a strange result, as shown in Figure 15.

Figure 15 Strange TrackPopupMenu Behavior
Figure 15 Strange TrackPopupMenu Behavior

      As you can see, the Microsoft Internet Explorer menu appears vertically, and each top-level item is an empty rectangle with no trace of the corresponding text item. This appearance makes it easy to detect a menu bar, but you lose the top-level item's text. For that reason, I have written the DumpMenu function, which takes a menu handle as a unique parameter. For each item and subitem, GetMenuItemInfo provides its ID (if any) between brackets, followed by its text. For example, if you take a snapshot before launching NOTEPAD.EXE and you make a comparison after it has started, you should get the results shown in Figure 16.
      In the current version of my MenuUsage tool, the code to generate text output is executed at the same time as TrackPopupMenu. Each time you double-click on a menu handle, the DumpMenu method writes the output using the TRACE method, as you can see in MenuUsageDlg.cpp. If you need to get the description without a debugging session or an OutputDebugString catcher, it isn't difficult to add a read-only edit control to the MenuUsage tool and copy the dump into it.
      As a final word about the MenuUsage tool, you should have noticed that the one and only menu bar of NOTEPAD.EXE appears as five different menu handles in the comparison listbox. This means that each of its submenus (File, Edit, Search, and Help) is stored internally using a menu handle, so don't be surprised by the number of listed menu handles you get when you're tracking down menu leaks.

Conclusion

      It would be nice to have the same tool for Windows NT and Windows 2000, but since the implementation of GDI and USER now lies in the kernel realm, the same simplistic approach is no longer possible. Under all Win32-based systems, a GDI object handle is a real 32-bit long value and the loop to check each possible value takes too much time to run. But even worse, such a handle is meaningless outside the process that created it.
      So what about enumerating resources other than GDI objects and menus? In addition to using DH.EXE and OH.EXE in the Windows Resource Kit (see Knowledge Base article Q243318, "How to Use Dh.exe to Troubleshoot User-Mode Memory Leaks", you can get a nifty tool called HandleEx from http://www.sysinternals.com, which you can use to enumerate kernel objects such as files and semaphores. HandleEx provides the list of kernel objects used by each running process, along with their names and types. HandleEx is supported on Windows NT and on Windows 2000. Unfortunately, HandleEx relies on a device driver whose interface is not documented by its creator, Mark Russinovich. In order to really make the detection of leaks easier, the presence of a button to take a snapshot and compare would be a killer feature. If Mark documents his driver interface, I would write it for free in HandleEx.
For related articles see:
"Give Me a Handle, and I'll Show You an Object"

For background information see:
Windows 95 System Programming Secrets by Matt Pietrek (IDG Books, 1995)
"Moving Window Manager and GDI into the Windows NT 4.0 Executive"

Christophe Nasarre is a technical manager for Business Objects in France. He has written several low-level tools for Windows since version 3.0. He can be reached at cnasarre@montataire.net.

From the March 2001 issue of MSDN Magazine



MSDN Magazine
Issues
2001
March
Resource Leaks: Detecting, Locating, and Repairing Your Leaky GDI Code