哪些System.Drawing类被视为GDI对象?

33

我一直有困难理解System.Drawing名称空间中的确切对象实际上对系统总GDI对象计数做出了贡献。例如,Matrix对象是否计数?GraphicsPathPen

为了测试这一点,在Form初始化时运行了以下代码。

public partial class Form1 : Form
{
    Timer timer = new Timer();
    List<Pen> pens = new List<Pen>();

    public Form1()
    {
        InitializeComponent();

        var r = new Random();
        for (int i = 0; i < 1000; i++)
        {
            var p = new Pen(
                Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)),
                (float)r.NextDouble() * 10);
            pens.Add(p);
        }

        timer.Interval = 30;
        timer.Tick += timer_Tick;
        timer.Start();
    }

    void timer_Tick(object sender, EventArgs e)
    {
        panel1.Invalidate();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        for (int i = 0; i < pens.Count; i++)
        {
            e.Graphics.DrawLine(pens[i], (int)(20 + i * 0.1), 20, (int)(50 + i * 0.1), 50);
        }
    }
}

我惊讶地发现我的应用程序的GDI对象计数为32(使用任务管理器>详细信息> GDI对象列进行测量)。 即使我保留列表并强制Paint事件使用每个生成的笔绘制单独的线条,情况也是如此。 我甚至尝试创建随机颜色以确保没有东西被重复使用,但GDI计数仍然稳定在32。

有很多帖子担心缓存和重用Pen实例和其他对象,但我不确定是否所有System.Drawing对象都会导致此计数。

也许现代GDI +实现实际上是懒惰的,并且只在您实际绘制某些内容时延迟分配?

更新: GSerg指出MSDN官方文档

当使用GDI +时,您无需像使用GDI时那样关注句柄和设备上下文。

似乎GDI +对GDI句柄进行了抽象处理,并尽可能避免使用它们。 这也与其他地方的报告一致,这些报告似乎表明GDI +仅在绝对需要时才会创建GDI句柄。 例如,只有在调用需要存在句柄的方法(例如GetHBitmap())时,Bitmap才由句柄支持。

似乎,在现代GDI +中,PenBrush和许多其他System.Drawing对象实际上并不会影响总的GDI对象计数。 当然,在C#中,如果它们没有被释放,它们仍会泄漏非托管内存,因为它们是由非托管GDI +对象支持的,但本机内存不像GDI内存那样严格。

如果当前解释未被争议,我将在接下来的几天内将其移到答案中。


2
我刚刚复制了你的测试,但是 GDIView 显示 0 个 GDI Pen 句柄(33 个 GDI 对象总数)。我只是认为这是由于 GDI+ 的工作方式不同造成的。顺便说一下,我在 Windows 7 上进行测试。https://imgur.com/a/QC1CGOb - Bradley Uffner
@BradleyUffner,这很令人印象深刻,而且很高兴知道在Windows 7上可以再现。我针对的是.NET 4.5+,所以我并不关心比那更早的操作系统。我只是感到困惑的是,许多反对缓存/重用实例的旧建议似乎不再适用。 - glopes
3
如果有其他重要信息需要加入问题中,您应该从聊天记录中复制并插入编辑中。 - Samuel Liew
我对你为什么需要知道感兴趣?不同的操作系统/框架组合可能会产生不同的结果。只要你处理掉需要这个的任何东西,你就应该没问题了。 - Matt Wilko
问题的范围仅限于Windows,并且在最新的.NET框架上验证了Win7和Win10的行为,因此存在一致性。该问题不是关于是否处置的问题,而是关于缓存的影响。在旧的Win32 API中,保持GDI句柄具有重要的性能影响,但只要使用GDI+,这些影响似乎就不那么重要了。 - glopes
1个回答

1
以下对象会导致创建 GDI 句柄:
  • 位图(Bitmap)
  • 画刷(Brush)
  • 设备上下文(DC)
  • 增强型图元文件(Enhanced Metafile)
  • 增强型图元文件 DC(Enhanced Metafile DC)
  • 字体(Font)
  • 内存 DC(Memory DC)
  • 图元文件(Metafile)
  • 图元文件 DC(Metafile DC)
  • 调色板(Palette)
  • 画笔(Pen)
  • 扩展画笔(Extended Pen)
  • 区域(Region)
此列表是使用此处的信息编制的: https://learn.microsoft.com/en-gb/windows/desktop/SysInfo/gdi-objects

4
这是非常误导人的。GDI+(又称System.Drawing)是GDI的基本替代品。它们保留了相同的对象模型,帮助程序员移植他们的代码,但实现方式却截然不同。在Windows版本之间发生了变化,并且使用较少的传统GDI对象来完成任务,因此不会像以前那样增加任务管理器的诊断负担。一个API很好的一点是,它只需要看起来相同,你就不必被困扰于第n-9个版本的实现问题。 - Hans Passant
除了硬件加速,@HansPassant。当您在Windows Vista以外的操作系统上使用GDI(而不是GDI +)时,您知道什么将被加速,什么不会被加速。使用GDI + / System.Drawing时,我无法确信除位图平铺之外的任何绘图都将在单个60Hz(16ms)帧内完成(好吧,在Windows 7+中除了平铺之外的所有GDI 2D操作都受到了限制。我仍然对此感到愤怒)。这只是有点令人担忧,因为140Hz甚至280Hz的显示器越来越受欢迎,但GDI仍停留在1993年。 - Dai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接