我的 hdc/hbitmap 的内存泄漏在哪里?

Posted

技术标签:

【中文标题】我的 hdc/hbitmap 的内存泄漏在哪里?【英文标题】:Where is the memory leak from my hdc/hbitmap? 【发布时间】:2019-04-02 07:49:38 【问题描述】:

所以我注意到我的部分代码在被调用时会泄漏大量内存,我试图找出它泄漏的位置或原因,但我陷入了死胡同。

我已尝试使用 Visual Studio 2017 调试器拍摄快照以找出泄漏发生的位置,但据此没有任何重大泄漏。我也试过 Deleaker,我曾经工作过,它告诉我我泄露了 HDC 和 HBITMAP,但无法告诉我有多少内存。

第一个函数是 GetScreenBmp 可能泄漏的地方,但我不是在正确释放所有内容吗?我知道我没有删除 hBitmap 但我需要返回它。这是内存泄漏的地方吗?

HBITMAP GetScreenBmp(HDC hdc, int screenPositionX, int screenPositionY, int screenSizeX, int screenSizeY) 
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
int nMousePositionX = 0, nMousePositionY = 0;

// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hCaptureDC = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, screenSizeX, screenSizeY);
HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
BOOL bOK = BitBlt(hCaptureDC, 0, 0, screenSizeX, screenSizeY, hdc, screenPositionX, screenPositionY, SRCCOPY | CAPTUREBLT);

SelectObject(hCaptureDC, hOld); // always select the previously selected object once done
DeleteObject(hOld);
DeleteDC(hCaptureDC);

return hBitmap;

第二部分是这段代码,我不完全确定我是否正确删除了所有内容。

HDC hdc = GetDC(0);
    HBITMAP hBitmap = GetScreenBmp(hdc, currentSplitInformationArray.screenPositionX, currentSplitInformationArray.screenPositionY, currentSplitInformationArray.screenSizeX, currentSplitInformationArray.screenSizeY);
    BITMAPINFO MyBMInfo =  0 ;
    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);

    // Get the BITMAPINFO structure from the bitmap
    if (0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) 
        MessageBox(NULL, "Resource not available\nDo you want to try again?", "Account Details", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
    

    // create the bitmap buffer
    BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];

    // Better do this here - the original bitmap might have BI_BITFILEDS, which makes it
    // necessary to read the color table - you might not want this.
    MyBMInfo.bmiHeader.biCompression = BI_RGB;
    MyBMInfo.bmiHeader.biHeight = currentSplitInformationArray.screenSizeY * -1;

    // get the actual bitmap buffer
    if (0 == GetDIBits(hdc, hBitmap, 0, currentSplitInformationArray.screenSizeY, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) 
        MessageBox(NULL, "Resource not available\nDo you want to try again?", "Account Details", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
    

    ::SendMessage(testingComparison, STM_SETIMAGE,
        (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);

    DeleteObject(&MyBMInfo);
    DeleteObject(hBitmap);
    ReleaseDC(NULL, hdc);
    delete[] lpPixels;

如果这是之前已经回答过的问题,或者答案很容易被搜索到,我很抱歉,但我已经尝试了几个小时来解决它。

【问题讨论】:

BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage]; -- 这是哪里的delete [] lpPixels;?此外,由于这是 C++,因此可以简单地使用 std::vector<BYTE> lpPixels(MyBMInfo.bmiHeader.biSizeImage);,然后使用 lpPixels.data() 指向缓冲区。这样可以避免内存泄漏的可能性。 另外,使用RAII等技术在对象超出范围时自动释放资源,而不必记住每次都调用DeleteObjectDeleteDC 对不起,我忘记了那部分,我已经用它更新了帖子。我使用 BYTE* 而不是 std::vector 因为在调试模式下 std::vector 有太多额外的开销,因为代码每秒运行 60 次。我知道在发布中已删除。我去看看那个 RAII。 std::vector 有“太多开销”是什么意思?如果您将该向量的声明移到该代码之外(希望您使用的是类),并且每次只调用resize() 而不是new BYTE[],那么您的代码速度将会提高,因为您不会调用分配器每一次。换句话说,vector::resize() 比您现在拥有的代码更智能地使用内存。 另外,如果这些函数中的任何一个抛出异常,你就会得到内存泄漏,因为delete [] lpPixels; 永远不会被调用。 RAII 的所有部分。 【参考方案1】:

好的,我找到了解决方案。 STM_SETIMAGE 消息返回之前的图像,你必须自己处理。 https://docs.microsoft.com/en-us/windows/desktop/Controls/stm-setimage

下次我可能应该学会更好地阅读文档,抱歉浪费了大家的时间。

::SendMessage(testingComparison, STM_SETIMAGE,
            (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);

简单地修复它

HBITMAP oldBitmap = (HBITMAP)::SendMessage(testingComparison, STM_SETIMAGE,
        (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
    DeleteObject(oldBitmap);

【讨论】:

【参考方案2】:

使用工具来跟踪您的泄漏/分配(顺便说一句。您没有发布一开始是如何发现泄漏的)。

由于您使用的是 Visual Studio C++,因此您可以使用内置工具。基本上这3行的组合就可以完成工作了。

    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );//to enable, safe to always set somewhere around program startup - on normal exit this will print whatever you leaked

    //_CrtDumpMemoryLeaks();//Dumps to see what has already been allocated

    //_CrtSetBreakAlloc(#number);//Use this to set breakpoint using the allocation number from heap dump to see where allocation takes place. If allocation happends before this line it will not work.

【讨论】:

我注意到当我调试程序时,进程内存在几秒钟内从 0-2Gb 变为内存泄漏。然后我尝试使用堆分析并拍摄快照,这表明我在循环之外有内存泄漏。然后我尝试了 Deleaker,我让它工作了两次,两次都说我泄露了 HDC 和 HBITMAP。但我会再试一次,也许我只是愚蠢而错过了什么。

以上是关于我的 hdc/hbitmap 的内存泄漏在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

Pandas:这里的内存泄漏在哪里?

内存泄漏在哪里? python - 如何在python中的多处理期间使线程超时?

这里的内存泄漏在哪里?

Instruments 说这种方法存在内存泄漏,但我不知道在哪里

Android:使用 MAT 查找内存泄漏

Silverlight + MVVM + 绑定 = 内存泄漏?