字体导致自定义控件中的GDI泄漏

时间:2013-04-28 10:31:19

标签: c# winforms custom-controls gdi resource-leak

我已经创建了一个自定义控件,如下所示。

public partial class TextBoxEx : TextBox
{
  public TextBoxEx()
  {
    InitializeComponent();
    Font = Utility.normalFont;
  }

   protected override void OnPaint(PaintEventArgs pe)
   {
    base.OnPaint(pe);
   }
 }
//A utility class to initialize font.   
class Utility
{

    internal static Font normalFont = new Font("Arial", 18);
}

我有两种形式Form1和Form2。此TextBoxEx已添加到Form2。我在单击Form1中的按钮时显示Form2。

持续显示和关闭Form2会导致我的应用程序中出现GDI泄漏。在使用GDI检测工具(Bear.exe)进行分析后,发现Font导致GDI泄漏。

我的问题是,

  1. 为什么即使调用TextBoxEx的Dispose()方法也不释放Font。(在关闭Form2时,将自动调用TextBoxEx的Dispose()方法。)
  2. 如何解决Font引起的GDI泄漏? (Font.Dispose()不能在TextBoxEx的Dispose()方法中调用。因为它在构造函数中抛出“参数无效”异常。)

1 个答案:

答案 0 :(得分:2)

大多数绘图对象非常便宜创建。例如,笔或画笔创建时间不会超过一微秒。这就是为什么你应该总是在你开始绘图时创建它们并在你完成绘图时处理它们。 使用语句强烈建议您这样做。

然而,Font类很难。它们便宜创建,Windows需要做很多工作来将您要求的字体映射到可用的字体集并加载TrueType大纲。 Winforms有一个解决方案,它缓存字体。您第一次使用它时会产生创建字体的成本。但是你可以处理它,但字体对象保留在字体缓存中。下次创建相同的字体时,您将从缓存中获得非常便宜的副本。

这也是WPF中的一个问题,因为它具有更丰富的字体支持,包括对OpenType轮廓的支持。它以不同的方式解决,WPF使用完全独立的进程来缓存字体。像任何WPF应用程序的字体缓存服务器一样。您将在任务管理器中看到此过程,它是PresentationFontCache.exe进程。

Anyhoo,任何类型的泄漏诊断程序都会被这个缓存混淆。它会认为您的应用程序正在泄漏字体,它会看到存储在缓存中的字体。当使用的字体数量无限制地增长时,您只会出现真正的泄漏,并最终导致程序崩溃。容易测试,Windows施加的配额低,一个进程不能创建超过10,000个绘图对象。因此,如果您有真正的泄漏,则无需长时间运行测试程序即可达到该配额。您还可以在任务管理器中看到此信息。查看+选择列,勾选GDI对象复选框。确保您的测试程序的编号稳定,并且不会超过几百,给予或接受。