GDI:原始 DC 位图更改,无法恢复

Posted

技术标签:

【中文标题】GDI:原始 DC 位图更改,无法恢复【英文标题】:GDI: Original DC bitmap changes, and can't restore 【发布时间】:2018-01-15 12:36:10 【问题描述】:

我看到一个问题,我无法在 DC 上重新选择原始位图,从而导致内存泄漏。指向原始位图的指针在整个程序中保持不变,但数据(来自 CBitmap::GetBitmap)从单色变为其他内容。我不知道位图何时真正改变,但系统中的某些东西导致了它。

CBitmap* cMyClass::mpOldBitmap;
CDC cMyClass::mCanvasDc;
CBitmap cMyClass::mCanvasBmp;

void cMyClass::Init()

    // One-time initialization
    CDC* pDc = GetDC();
    mCanvasDc.CreateCompatibleDC(pDc);
    mCanvasBmp.CreateCompatibleBitmap(pDc, 10, 10);
    mpOldBitmap = mCanvasDc.SelectObject(&mCanvasBmp);
    ReleaseDC(pDc);

    BITMAP bitmap;
    mpOldBitmap->GetBitmap(&bitmap); // A monochrome bitmap, as expected.


void cMyClass::Recreate(int newW, int newH)

    // 1. Delete existing bitmap:
    if (mpOldBitmap)
    
        BITMAP bitmap;
        mpOldBitmap->GetBitmap(&bitmap); // This is no longer the monochrome bitmap. It is 8bpp, with random size.

        CBitmap* pCurrBmp = mCanvasDc.SelectObject(mpOldBitmap); // This fails (NULL). I can't de-select my bitmap.
        mCanvasBmp.DeleteObject(); // This fails too, causing memory leak. Actually, it fails in CE6, but not in Win32. Regardless, both platforms will have a memory leak.
    

    // 2. Recreate the bitmap with new size:
    
        CDC* pDc = GetDC();
        mCanvasBmp.CreateCompatibleBitmap(pDc, newW, newH);
        ReleaseDC(pDc);
    

    // 3. Finalize
    mpOldBitmap = mCanvasDc.SelectObject(&mCanvasBmp);

    任何已知的可能发生这种情况的场景? 位图数据发生变化时有什么调试技巧可以破解?

注意:在代码中,我提到了“这失败了”。我删除了返回值上的断言以使代码可读。


编辑:我用来修复它的解决方案是使用 CDC:SaveDC 和 CDC::RestoreDC 而不是存储指针。内存泄漏消失了,每个 GDI 调用都通过了。但是我还是很好奇为什么原始代码会泄露。据我所知,指向默认位图的指针应该是默认的单色位图,在 GDI 世界中可能是全局的

【问题讨论】:

minimal reproducible example 必填。 我认为你的问题是你不能像这样操作(重新创建)一个实例变量。您需要一个指向 CBitmap 的指针,即 CBitmap* mCanvasBmp 并“删除”它并像 mCanvasBmp = new CBitmap 每次一样重新创建它。 【参考方案1】:

让我们看看 OP 的代码。

mpOldBitmap = mCanvasDc.SelectObject(mCanvasBmp);

因为 mCanvasBmp 是一个 CBitmap 对象(不是指向 CBitmap 的指针),所以首先调用 HGDIOBJ 运算符,然后是返回 HGDIOBJCDC::SelectObject(HGDIOBJ) 和不是 CBitmap*。这应该会产生转换编译器错误。如果将返回值转换为 CBitmap* 也是错误的。

摆脱问题的正确方法是传递指针。

mpOldBitmap = mCanvasDc.SelectObject(& mCanvasBmp);

这种情况将被称为 CDC::SelectObject(CBitmap* pBitmap),它返回一个 CBitmap*

// 我希望它很清楚。 :)

【讨论】:

对不起,这是我在尝试简化 SO 代码时的拼写错误。我已经更正了描述。我还添加了我找到的解决方案,但我仍然很好奇为什么原来的解决方案不起作用。

以上是关于GDI:原始 DC 位图更改,无法恢复的主要内容,如果未能解决你的问题,请参考以下文章

.net 位图改变图像的颜色

什么是,为什么我必须在清理时将位图 hdc 或内存 dc 恢复到默认状态? [复制]

GDI双缓冲绘图

如何使用 Powershell 更改 Windows 10 中的电源计划并在长脚本后恢复为原始设置?

我可以更改 PayPal 按钮上的事件侦听器以激活复活节彩蛋,然后稍后将其恢复为原始状态吗?

如何通过 Bitmap::GetHBITMAP 将位图转换为带有 alpha 的 HBITMAP?