我是否总是需要调用 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()
来简化原始对象的恢复:
保存、还原和重置设备上下文