【问题标题】:GDI handles in a DotNET applicationDotNET 应用程序中的 GDI 句柄
【发布时间】:2011-01-29 19:02:44
【问题描述】:

我的纯 DotNET 库在非托管桌面应用程序中作为插件运行。我一直在收到稳定(虽然很低)的崩溃报告流,这些报告似乎表明 GDI 句柄存在问题(错误消息中的字体等。恢复为系统字体,各种控件的显示出现故障,不久后出现大规模崩溃)。

我的表单很少有控件,但我在用户控件中做了很多 GDI+ 绘图。有什么好方法可以告诉我正在使用多少个句柄,甚至泄漏?

谢谢, 大卫

【问题讨论】:

  • 一个好的开始是用一个典型的例子来更新问题,说明你的绘图代码是什么样的。这样你就可以得到一些具体的答案,你也许可以改进。

标签: .net memory-leaks count gdi handle


【解决方案1】:

从 Ray Vega 的回答中以GDIView 开始,我找到了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 告知泄漏的是字体对象;然后我在我们的日志代码中添加了对GetGuiResources 的调用,以检测触发对象创建的时间点。

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

【讨论】:

    【解决方案2】:

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

    GDIView 是一个独特的工具,可以显示 GDI 句柄列表(画笔、 笔、字体、位图等) 由每个进程打开。它显示 每种 GDI 的总数 处理,以及详细 每个句柄的信息。这 该工具对开发人员很有用 需要跟踪 GDI 资源泄漏 他们的软件。


    (来源:nirsoft.net

    注意默认情况下自动刷新是禁用的,但是可以在特定的时间间隔内启用和配置:Options -> Auto Refresh -> Every [N] seconds

    【讨论】:

      【解决方案3】:

      除了性能监视器,您还可以试试旧的任务管理器。

      选中Process选项卡并单击View > Select Columns...并检查GDI对象。

      【讨论】:

      • 感谢 Paolo,不幸的是,我的应用程序在一个更大的应用程序中作为插件运行,它希望能够查看哪些句柄仅用于我的代码。我不确定这是否可能,但它比总体概述更可取。
      【解决方案4】:

      我过去也曾处理过同样的问题。为了检查您的应用程序分配了多少 GDI 对象,您可以使用名为 GDIUsage 的免费工具。

      在我的例子中,应用程序崩溃了,因为它分配了超过 10.000 个 GDI 对象,这在 Windows XP 中是一个硬性限制。可能值得研究一下。

      我在这里写过关于这个问题的博客:
      http://megakemp.com/2009/02/25/gdi-memory-leak-in-windows-forms/

      【讨论】:

      • 谢谢!这会让我开始。
      【解决方案5】:

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

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

      【讨论】:

      • 如果托管代码离开 GC 去调用Dispose 方法,那么也可能会导致问题,因为 GC 在并行线程中运行并且在其方便时运行。这就是为什么在托管代码中使用 GDI 对象时,最好将它们包装在 using 块之间。
      • 感谢 nobugz。 “这不应该在托管程序中真正发生”。我知道,而且我很确定我不会在我创建的每个 Pen、Brush 和 GraphicsPath 上调用 Dispose。由于未加载我的插件时不会发生报告的问题,因此我假设这是我的错,而不是非托管应用程序。
      【解决方案6】:

      如果您还没有这样做,请确保对您正在使用的任何 GDI+ 绘图对象调用 IDisposable.Dispose。您通常会使用 C# using 构造来执行此操作,例如:

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

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

      未能以这种方式调用 Dispose 是潜在的句柄泄漏,因为在垃圾收集器认为合适之前不会回收句柄。

      【讨论】:

        【解决方案7】:

        GDIObj,冯远提供的免费实用程序,作为他的书的示例程序,Windows Graphics Programming: Win32 GDI and DirectDraw 可能有用。

        与任务管理器不同,它提供了对不同 GDI 句柄类型(包括 DC、区域、位图、调色板、字体、画笔等)的进一步细分计数。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-10
          • 2011-10-12
          • 2011-03-07
          • 2010-09-13
          • 2012-12-04
          • 2012-02-20
          • 1970-01-01
          • 2015-04-17
          相关资源
          最近更新 更多