删除DC是否自动取消选择对象



我是否总是需要调用 SelectObject 将原始对象还原到 DC 中(并使我的对象可供删除(,即使我无论如何都要删除 DC...?

例如

  // Create DC
  HBITMAP hBitmap = CreateCompatibleBitmap (hDC, rect.Width(), rect.Height());
  HGDIOBJ hOldBitmap = SelectObject(hMemDC, hBitmap);

  // ... Do some other stuff with the DC
  // DO I NEED THIS LINE HERE???
  SelectObject(hMemDC, hOldBitmap);
  // Tidy up
  DeleteDC(hMemDC);
  DeleteObject(hBitmap);  // This DOES return TRUE even without the SelectObject line...

"DeleteDC"是否会自动取消选择已选择的对象,以便在删除 DC 后可以删除它们?

谢谢

我对这个问题进行了自己的调查。根据他们的说法,标记的答案是不准确的,至少在这个陈述中:

一个直流...将相应地在释放任何当前选定的对象时释放它,因为它需要原始对象。

当您将刚刚创建的内存

位图选择到刚刚创建的内存 DC 中时,内存 DC 不拥有位图(至少在内存管理意义上( - 它将其标记为"已使用",因此无法通过DeleteObject调用将其删除(可能,通过增加位图的一种引用计数(。如果先删除内存 DC - 它不会删除内存位图:GetGuiResources(GR_GDIOBJECTS) 返回的对象计数减少 1,即仅针对 DC 本身,而不是 2(对于 DC 和位图(。随后的DeleteObject(hMemBitmap)调用会相应地减少 GDI 对象计数。

至于此评论中提到的"默认 1x1 位图泄漏",似乎也是错误的:此位图不是为每个内存 DC 单独创建的。相反,它是 Raymond Chen 很好地解释的所谓"库存位图":根据他的"神秘股票位图"文章,此位图是 GDI 用于各种目的的单例,并且是唯一可以选择到许多 DC 中的位图。除了将其选择到 DC 中之外,您无法删除它或以某种方式使用它。尽管DeleteObject在此位图的句柄上返回 TRUE,但它不会影响 GDI 对象计数。在删除该 DC 之前选择此位图返回到内存 DC 只是一种好习惯,实际上不是必需的:选择它会增加其使用引用计数,然后删除 DC 会减少该引用计数,从而导致没有增益(参考值与从内存 DC 中"取消选择"常用位图后相同(。

我制作了一个PoC测试程序,并在各种版本的Windows(从WinXP SP3到最新的Win10更新(中对其进行了检查 - 结果非常一致。

总结一下:

  • 原始问题的答案是"是的,确实如此">

  • // DO I NEED THIS LINE HERE???问题的答案是"不,您没有 - 前提是您确定旧位图的句柄是常用位图的句柄">

DC 不维护所选对象的历史记录。 例如,想想如果你在绘制某些东西时多次SelectObject()不同的字体或画笔会发生什么。 DC 只知道当前对象,而不知道前面的任何对象。 这就是为什么在释放 DC 之前,您必须始终使用 SelectObject() 来恢复您替换的任何对象。 DeleteDC()不会为你做那个恢复。

这在文档中有明确说明:

SelectObject功能:

此函数返回以前选择的指定类型的对象。应用程序在使用新对象完成绘制后,应始终将新对象替换为原始默认对象。

对图形对象的操作:

其中每个函数都返回一个标识新对象的句柄。应用程序检索句柄后,必须调用 SelectObject 函数来替换默认对象。但是,应用程序应保存标识默认对象的句柄,并在不再需要新对象时使用此句柄替换新对象。当应用程序使用新对象完成绘制时,它必须通过调用 SelectObject 函数来还原默认对象,然后通过调用 DeleteObject 函数删除新对象。无法删除对象会导致严重的性能问题。

DC 拥有最初创建它时使用的对象。 因此,当它被释放时,它将释放任何当前选定的对象,因为它需要原始对象。 无法还原原始对象将导致代码泄漏和可能的其他故障。

如果要在绘制时替换/恢复多个对象,请考虑使用SaveDC()RestoreDC()来简化原始对象的恢复:

保存、还原和重置设备上下文

最新更新