GDI handles in a DotNET application

2019-04-08 07:39发布

问题:

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:

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.com/2009/02/25/gdi-memory-leak-in-windows-forms/



回答2:

Starting with GDIView from Ray Vega's answer, I found this tip:

[DllImport("User32")] 
extern public static int GetGuiResources(IntPtr hProcess, int uiFlags);

  public static void GetGuiResourcesGDICount()      
  { 
      //Return the count of GDI objects.          
      Console.WriteLine("GDICount"+GetGuiResources(System.Diagnostics.Process.GetCurrentProcess().Handle, 0));      
  }

  private void button1_Click(object sender, System.EventArgs e)
  {
      GetGuiResourcesGDICount();
  }

GDIView informed that it was font objects that were being leaked; I then added calls to GetGuiResources into our logging code to detect at which point the object creation was being triggered.

In our case, we had the text of a Label control being updated when its parent UserControl was hidden in a background window. This would cause GDI to leak font handles. To fix it, we changed our logic to not update the Label unless it was currently visible on screen. To determine whether it was visible, we keep a record of when the UserControl was last painted.



回答3:

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.



回答4:

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 http://www.nirsoft.net/utils/gdiview.gif

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



回答5:

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.



回答6:

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.



回答7:

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.)