views:

313

answers:

6

My pure DotNET library runs as a plugin inside an unmanaged desktop application. I've been getting a steady (though low) stream of crash reports that seem to indicate a problem with GDI handles (fonts in error messages etc. revert to the system font, display of all sorts of controls break down, massive crash shortly after).

My Forms have few controls, but I do a lot of GDI+ drawing in User controls. What's a good way to tell how many handles I'm using, or even leaking?

Thanks, David

+1  A: 

It is easy to see from TaskMgr.exe, Processes tab. View + Select Columns, tick GDI Objects.

Your description indeed matches a handle leak. That shouldn't really happen in a managed program, the finalizer should take care of you forgetting to call Dispose(). Unless you don't consume a lot of garbage collected heap space. It could also be the unmanaged app leaking handles, they very often do.

Hans Passant
If the managed code leaves the GC to call the `Dispose` method then it may also lead to problems, because GC runs in a parallel thread and runs at its convenience. That's why when using GDI objects in managed code it's a good practice to wrap them between a `using` block.
Paulo Santos
Thanks nobugz. "That shouldn't really happen in a managed program". I know, and I'm pretty sure I don't call Dispose on *every* Pen and Brush and GraphicsPath I create. Since the reported problems do not occur when my plugin isn't loaded, I'm assuming it's my fault rather than the unmanaged app.
David Rutten
+1  A: 

Besides the performance monitor, you can try the good old Task Manager.

Check the Process tab and click View > Select Columns... and check the GDI objects.

Paulo Santos
Thanks Paolo, unfortunately my app is running as a plugin inside a larger app, and it want to be able to see which handles are used for only my code. I'm not exactly sure whether not this is possible, but it would be preferable to a grand-total overview.
David Rutten
+1  A: 

If you are not already doing so, make sure you call IDisposable.Dispose on any GDI+ drawing objects you are using. You would usually do this with the C# using construct, e.g.:

using(Brush brush = ...)
{
    ...
}

Static code analysis tools such as FxCop or the version built into Team System editions of Visual Studio can help to detect cases where you fail to call Dispose.

Failure to call Dispose in this way is a potential handle leak, as the handle won't be reclaimed until the garbage collector sees fit.

Joe
+1  A: 

I've had to deal with the same kind of problem in the past. In order to inspect how many GDI objects your application is allocating you can use a free tool called GDIUsage.

In my case the application was crashing because it was allocating more than 10.000 GDI objects, which is a hard limit in Windows XP. It might be worth looking into it.

I've blogged about this problem here:
http://megakemp.wordpress.com/2009/02/25/gdi-memory-leak-in-windows-forms/

Enrico Campidoglio
Thanks! This will get me started.
David Rutten
+1  A: 

Take a look at the GDIView (it's freeware):

GDIView is a unique tool that displays the list of GDI handles (brushes, pens, fonts, bitmaps, and others) opened by every process. It displays the total count for each type of GDI handle, as well as detailed information about each handle. This tool can be useful for developers that need to trace GDI resources leak in their software.

alt text

Note that the auto-refresh is disabled by default, but it can enabled and configured for specific intervals: Options -> Auto Refresh -> Every [N] seconds

Ray Vega
+1  A: 

GDIObj, a free utility provided by Feng Yuan as a sample program for his book, Windows Graphics Programming: Win32 GDI and DirectDraw might be useful.

Unlike Task Manager, it provides counts of a further breakdown of different GDI handle types including DC, Region, Bitmap, Palette, Font, Brush, etc.

(However, it only provides count info while GDIView provides more detail about the handles.)

Ray Vega