我的窗体有几个控件,但我在用户控件中进行了大量的GDI+绘图。如何有效地确定我使用了多少句柄,甚至是泄漏了多少?
谢谢, 大卫
从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
最后一次绘制的时间。
请看GDIView(它是免费软件):
GDIView 是一款独特的工具,它显示每个进程打开的 GDI 句柄(画刷、笔、字体、位图等)列表。它显示每种类型的 GDI 句柄的总数,以及每个句柄的详细信息。这个工具对于需要跟踪其软件中 GDI 资源泄漏的开发人员非常有用。
(来源:nirsoft.net)
注意,自动刷新默认情况下被禁用,但可以启用并配置为特定的时间间隔:选项 -> 自动刷新 -> 每 [N] 秒
从TaskMgr.exe的进程选项卡中很容易看出来。选择“查看”+“选择列”,勾选GDI对象。
您的描述确实与句柄泄漏相匹配。在托管程序中,这不应该真正发生,终结器应该负责您忘记调用Dispose()。除非您没有消耗大量垃圾收集堆空间。它也可能是未经处理的应用程序泄漏句柄,它们经常这样做。
Dispose
方法,那么也可能会导致问题,因为GC在并行线程中运行,并在其方便时运行。这就是为什么在托管代码中使用GDI对象时,将它们包装在using
块之间是一个好的实践。 - Paulo Santos如果你还没有这么做的话,请确保对任何你正在使用的GDI+绘图对象调用IDisposable.Dispose方法。你通常可以使用C#中的using
结构来实现,例如:
using(Brush brush = ...)
{
...
}
静态代码分析工具,如FxCop或内置于Visual Studio Team System版本中的版本,可以帮助检测您未调用Dispose的情况。
以这种方式未调用Dispose可能会导致潜在的句柄泄漏,因为直到垃圾回收器认为合适时,该句柄才会被回收。
GDIObj是冯源提供的一个免费实用程序,作为他的书《Windows图形编程:Win32 GDI和DirectDraw》的示例程序,可能会很有用。
与任务管理器不同,它提供了更详细的不同GDI句柄类型的进一步细分计数,包括DC、Region、Bitmap、Palette、Font、Brush等。
(然而,它只提供计数信息,而GDIView则提供有关句柄的更多详细信息。)