DotNET应用程序中的GDI句柄

6
我的纯DotNET库作为插件在一个非托管的桌面应用程序内运行。我一直收到稳定(尽管很少)的崩溃报告,似乎表明存在GDI句柄问题(错误消息中的字体等恢复为系统字体,各种控件的显示崩溃,不久后发生大规模崩溃)。
我的窗体有几个控件,但我在用户控件中进行了大量的GDI+绘图。如何有效地确定我使用了多少句柄,甚至是泄漏了多少?
谢谢, 大卫

1
一个好的开始是更新问题,附上你绘图代码的典型示例。这样,你可能会得到一些具体的答案,可以帮助你改进。 - Fredrik Mörk
7个回答

6

从Ray Vega的回答中开始使用GDIView,我发现了这个提示

[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提醒我们存在泄漏的字体对象;然后我在日志记录代码中添加了调用GetGuiResources,以便检测对象创建被触发的时刻。

在我们的情况下,当父级UserControl被隐藏在一个背景窗口中时,Label控件的文本被更新,这将导致GDI泄漏字体句柄。为了解决这个问题,我们改变了逻辑,除非Label当前正在屏幕上显示,否则不更新它。为了确定它是否可见,我们记录了UserControl最后一次绘制的时间。


5

请看GDIView(它是免费软件):

GDIView 是一款独特的工具,它显示每个进程打开的 GDI 句柄(画刷、笔、字体、位图等)列表。它显示每种类型的 GDI 句柄的总数,以及每个句柄的详细信息。这个工具对于需要跟踪其软件中 GDI 资源泄漏的开发人员非常有用。

alt text
(来源:nirsoft.net

注意,自动刷新默认情况下被禁用,但可以启用并配置为特定的时间间隔:选项 -> 自动刷新 -> 每 [N] 秒


3
除了性能监视器,您可以尝试使用老牌的任务管理器。
请检查“进程”选项卡并单击“查看”>“选择列...”,然后勾选GDI对象。

谢谢Paolo,不幸的是我的应用程序作为一个插件运行在一个更大的应用程序中,并且它想要能够看到哪些句柄仅用于我的代码。我不确定这是否可能,但相对于总体概述,这将是更可取的。 - David Rutten

2
我以前也遇到过同类问题。为了检查你的应用程序分配了多少GDI对象,你可以使用一个免费工具叫做 GDIUsage
在我的情况下,应用程序崩溃是因为它分配了超过10,000个GDI对象,这是Windows XP中的硬限制。这可能值得研究一下。
我在这里写了关于这个问题的博客:
http://megakemp.com/2009/02/25/gdi-memory-leak-in-windows-forms/

1

从TaskMgr.exe的进程选项卡中很容易看出来。选择“查看”+“选择列”,勾选GDI对象。

您的描述确实与句柄泄漏相匹配。在托管程序中,这不应该真正发生,终结器应该负责您忘记调用Dispose()。除非您没有消耗大量垃圾收集堆空间。它也可能是未经处理的应用程序泄漏句柄,它们经常这样做。


2
如果托管代码离开GC调用Dispose方法,那么也可能会导致问题,因为GC在并行线程中运行,并在其方便时运行。这就是为什么在托管代码中使用GDI对象时,将它们包装在using块之间是一个好的实践。 - Paulo Santos
感谢nobugz的回复。“在托管程序中,这种情况不应该发生”。我知道,我很确定我并没有在每个Pen、Brush和GraphicsPath上都调用Dispose。由于报告的问题在我的插件未加载时不存在,我认为问题是出在我的代码上而不是那个非托管的程序上。 - David Rutten

1

如果你还没有这么做的话,请确保对任何你正在使用的GDI+绘图对象调用IDisposable.Dispose方法。你通常可以使用C#中的using结构来实现,例如:

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

静态代码分析工具,如FxCop或内置于Visual Studio Team System版本中的版本,可以帮助检测您未调用Dispose的情况。

以这种方式未调用Dispose可能会导致潜在的句柄泄漏,因为直到垃圾回收器认为合适时,该句柄才会被回收。


1

GDIObj是冯源提供的一个免费实用程序,作为他的书《Windows图形编程:Win32 GDI和DirectDraw》的示例程序,可能会很有用。

与任务管理器不同,它提供了更详细的不同GDI句柄类型的进一步细分计数,包括DC、Region、Bitmap、Palette、Font、Brush等。

(然而,它只提供计数信息,而GDIView则提供有关句柄的更多详细信息。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接