我已经创建了一个自定义控件,如下所示:
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)分析,发现是该字体导致了GDI泄漏。
我的问题是,
- 为什么即使调用TextBoxEx的Dispose()方法,字体也没有被释放。(关闭Form2时,将自动调用TextBoxEx的Dispose()方法)。
- 如何解决由字体引起的GDI泄漏?在TextBoxEx的Dispose()方法中不能调用Font.Dispose()。因为它在构造函数中抛出"Parameter is not valid"异常)。
大多数绘图对象非常便宜创建。例如,一支笔或一支刷子的制作时间不超过一微秒。这就是为什么你应该总是在你开始绘制时创建它们并在完成绘制时处理它们。using语句强烈建议您这样做。
Font类是困难的。他们是不便宜创建,Windows需要做大量的工作来映射字体你要求的可用的字体集和加载TrueType大纲。Winforms有一个解决方案,它缓存字体。您将在第一次使用它时产生创建字体的成本。但你可以处理它但字体对象会留在字体缓存中。下次创建相同字体时,您将从缓存中获得非常便宜的副本。
这在WPF中也是一个问题,因为它有更丰富的字体支持,包括对OpenType轮廓的支持。这是一个不同的解决方法,WPF使用一个完全独立的进程来缓存字体。就像任何WPF应用程序的字体缓存服务器一样。你会在任务管理器中看到这个进程,它是PresentationFontCache.exe进程。
无论如何,任何类型的泄漏诊断程序都会被这个缓存所迷惑。它会认为你的应用程序在泄漏字体,它会看到存储在缓存中的字体。只有当使用的字体数量无限制地增长并最终导致程序崩溃时,才会出现真正的泄漏。易于测试,Windows强加的配额很低,一个进程不能创建超过10,000个绘图对象。所以你不需要运行你的测试程序很长时间来达到配额,如果你有一个真正的泄漏。您也可以在任务管理器中看到它。查看+选择列,勾选GDI对象复选框。确保你的测试程序的数量是稳定的,不会超过几百个。