我是否总是需要调用SelectObject将原始对象恢复为DC(并使我的对象可以删除),即使我即将删除DC ...?
e.g。
// 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后可以删除它们?
由于
答案 0 :(得分:3)
DC不会保留所选对象的历史记录。例如,考虑如果在绘制内容时多次SelectObject()
使用不同的字体或画笔会发生什么。 DC仅知道当前对象,而不知道任何先前的对象。这就是为什么在释放DC之前,必须总是使用SelectObject()
来恢复您替换的任何对象。 DeleteDC()
不会为你做那次修复。
文档中明确说明了这一点:
此函数返回先前选定的指定类型的对象。 在使用新对象完成绘制后,应用程序应始终使用原始默认对象替换新对象。
Operations on Graphic Objects:
这些函数中的每一个都返回一个标识新对象的句柄。在应用程序检索句柄后,它必须调用SelectObject函数来替换默认对象。但是,应用程序应保存标识默认对象的句柄,并在不再需要时使用此句柄替换新对象。 当应用程序使用新对象完成绘制时,它必须通过调用SelectObject函数来恢复默认对象,然后通过调用DeleteObject函数删除新对象。未能删除对象会导致严重的性能问题。
DC拥有最初创建的对象。因此,它将在释放时释放所有当前选定对象,因为它期望原始对象。无法恢复原始对象将导致代码中的泄漏和可能的其他故障。
如果您要在绘图时替换/恢复多个对象,请考虑使用SaveDC()
和RestoreDC()
来简化原始对象的恢复:
答案 1 :(得分:0)
我已经对这个主题进行了自己的调查。根据他们的说法,标记的答案是不准确的,至少在这个声明中是这样的:
<块引用>A DC ... 将在释放时相应地释放任何当前选定的对象,因为它期待原始对象。
当你选择一个刚刚创建的内存位图到一个刚刚创建的内存 DC 时,内存 DC 并不拥有这个位图(至少,在内存管理的意义上) ) - 它将它标记为“已使用”,因此它不能被 DeleteObject
调用删除(可能,通过增加位图的一种引用计数)。如果您先删除内存 DC - 它不会删除内存位图:GetGuiResources(GR_GDIOBJECTS)
返回的对象计数减 1,即仅针对 DC 本身,而不是减 2(对于DC 和位图)。随后的 DeleteObject(hMemBitmap)
调用相应地减少了 GDI 对象计数。
至于 in this comment 中提到的“默认 1x1 位图泄漏”,似乎也是错误的:这个位图不是为每个内存 DC 单独创建的。相反,它是Raymond Chen很好解释的所谓“stock bitmap”:根据his "The mysterious stock bitmap" article,这个位图是GDI用于各种目的的单例,它是唯一可以选择到许多DC中的位图。除了将其选入 DC 之外,您无法删除它或以任何方式使用它。尽管 DeleteObject
在此位图的句柄上返回 TRUE,但它不影响 GDI 对象计数。在删除那个 DC 之前选择这个位图回到内存 DC 只是一种好习惯,实际上不是必需的:选择它会增加它的使用引用计数,然后删除 DC 会减少引用计数,这导致没有增益(参考值与从内存 DC 中“取消选择”库存位图后的值保持不变)。
我制作了一个 PoC 测试程序,并在各种版本的 Windows(从 WinXP SP3 到最新的 Win10 更新)中进行了检查 - 结果非常一致。
总结:
原始问题的答案是“是的,确实如此”
// DO I NEED THIS LINE HERE???
问题的答案是“不,您不知道 - 前提是您确定旧位图的句柄是库存位图的句柄”