如何以及何时为WinForms控件处理Font

当分配任何控件的Font属性时,静态代码分析工具(来自HP的Fortify)抱怨Visual Studio Designer生成的WinForms代码。 分析工具抱怨:

line 143: this.mCopyrightLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); 

AboutWindowForm.cs中的函数InitializeComponent()无法正确处理第143行上Font()分配的非托管系统资源。

说明:

该程序无法正确处置使用非托管系统资源的托管对象。 无法正确处置使用非托管系统资源的托管对象至少有两个常见原因:

  • 错误条件和其他特殊情况。
  • 混淆程序的哪个部分负责释放资源。

在这种情况下,有一些程序路径,在第143行的AboutWindowForm.cs中分配的资源没有正确处理。

托管.NET对象的一小部分使用非托管系统资源。 .NET的垃圾收集器可能无法以可预测的方式释放原始托管对象。 因此,应用程序可能耗尽可用内存,因为垃圾收集器不知道非托管资源所消耗的内存。 大多数非托管资源泄漏问题导致一般软件可靠性问题,但如果攻击者可以故意触发非托管资源泄漏,则攻击者可能会通过耗尽非托管资源池来启动拒绝服务攻击。

我已经搜索了关于在Windows窗体中处理字体一段时间的主题,这些是我收集的要点:

  1. 创建字体将占用GDI对象,这是一个非托管资源,因此在不再需要时释放非常重要
  2. 由于GDI对象昂贵且稀缺,WinForm会缓存它们。
  3. Dispose WinForms控件还将处理其所有子控件并“释放获得的非托管资源”
  4. 如果表格是无模式的,表格将在关闭时处理

因此,我想得出结论,在VS生成的代码中没有必要显式分配给控件的Font,并且我们不应该这样做,因为字体是缓存的?

我创建了一个非常简单的表单测试程序:通过单击一个按钮,我创建了一个使用不同字体的新空表单。 关闭新打开的表单后,任务管理器中的GDI对象计数立即关闭。 这是上述要点的证据,不是吗?

然而静态分析仪似乎不相信。 它认为Font最终将由GC发布。 它还认为这对非托管资源不利,因为消耗的内存位于GC的知识之外,因此GC不会及时触发,因为GC感觉没有内存压力。 这为攻击者提供了故意触发耗尽非托管池的机会。

你能帮我理解分析仪给出的解释吗? 它对WinFroms有效吗? 手动处理创建的每个Font会很繁琐。

确切地说:

在控件的Dispose期间是否立即明确地处理Font,或者Control释放对Font的引用并让GC处理所有左边的?

谢谢!

我的问题的进一步更新:我做的另一个实验是:我在TaskManager和内存分析器中监视我的测试WinForm应用程序。 主窗体有一个按钮,用于打开另一个窗体,其单击时字体不同。 我注意到当我单击按钮并打开新窗体时,TaskManager中的GDI对象计数会增加。 因此,内存分析器观察到Font对象的计数。

但是,当我关闭新窗体时,TaskManager中的GDI对象数量立即下降。 内存探查器中Font对象的数量没有变化,这意味着GC没有发生。 但是,这些Font对象在内存分析器中被标记为“已处置但未收集”。

它给我这种感觉,当Form关闭时,Font对象设置为WinForms Control的Font属性已被正确处理。 这似乎是幕后的逻辑

  1. WinForm在关闭时处理一个表单(明确地,不是由GC)
  2. 处理表单将递归处理其所有子控件
  3. Dispose a Control将释放与其Font属性关联的Font对象(同样,显式,不是GC)

但这只是间接certificate+我的分析。 我没有像源代码或官方文档那样的直接证据来支持它。

您可以通过包含使用构造模式来控制Font的处理。 在调用main中,您应该将Application包装在Form的using构造中。

例:

  Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FrmMainUtility()); 

对此:

  Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); using (FrmMainUtility form = new FrmMainUtility()) { Application.Run(form); } 

试试吧!

当Form关闭时,GC将自动处理,无需显式处理字体