使用GDI +删除位图和CLSID对象时,C ++内存管理失败

时间:2019-01-28 23:50:22

标签: c++ memory-leaks bitmap gdi+

我无法管理在屏幕快照对象类中创建的Bitmap和CLSID对象的内存。这两个都来自GDI +库。标头列出了Screenshot.h

中的以下私有变量
#include <gdiplus.h>
#include <iostream>
#include <fstream>
#include <string>
#include "windows.h"
#pragma once
#pragma comment(lib, "gdiplus.lib")


using namespace std;
using namespace Gdiplus;


class Screenshot
{
private:
    HDC dc, memdc, fontdc;
    HBITMAP membit;
    Bitmap* bmpPtr;
    CLSID clsid;
ULONG_PTR gdiplusToken;
    int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

public:
    Screenshot();
    ~Screenshot();
    void TakeScreenshot(string userAction, string winName, long xMousePos, long yMousePos, long long tStamp);
    void SaveScreenshot(string filename);
    void memoryManagement();
};

然后,当我的主程序拍摄屏幕截图时,这些值将用TakeScreenshot()填充,但尚未保存到磁盘

void Screenshot::TakeScreenshot(//redacted for readibility) {
GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    HWND hwnd = GetDesktopWindow();
    dc = ::GetDC(0);
    int scaleHeight, scaleWidth = 0;        
    int Height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    int Width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    scaleHeight = Height + (0.1 * Height);
    memdc = CreateCompatibleDC(dc);
    membit = CreateCompatibleBitmap(dc, Width, scaleHeight);
    HBITMAP bmpContainer = (HBITMAP)SelectObject(memdc, membit);
    BitBlt(memdc, 0, 0, Width, Height, dc, 0, 0, SRCCOPY);

    //Other code that adds fonts, etc. Does not invoke bmpPtr

    bmpPtr = new Bitmap(membit, NULL);
    GetEncoderClsid(L"image/jpeg", &clsid);

如果保存了屏幕截图,则另一个函数SaveScreenshot()使用bmpPtr-> Save()并在其内部调用Gdiplus shutdown。但是,某些屏幕快照会从队列(STL队列)中弹出,并从内存中弹出而不是保存,如下所示:

void ManageQueue(Screenshot& ssObj)
{
    //If queue contains 30 screenshots, pop off first element and push new object
    //Else just push new object
    if (screenshotQueue.size() == MAX_SCREENSHOTS)
    {
        screenshotQueue.front().memoryManagement();
        screenshotQueue.pop();
        screenshotQueue.push(ssObj);
    }
    else
    {
        screenshotQueue.push(ssObj);
    }
}

我写了一个MemoryManagement()函数来执行必要的发布和删除操作,然后弹出屏幕截图。如果屏幕快照已保存,则不会调用此函数:

void Screenshot::memoryManagement()
{
    delete bmpPtr;
    delete &clsid;
    ReleaseDC(NULL, memdc);
    DeleteObject(fontdc);  
    DeleteObject(memdc);
    DeleteObject(membit);
}

当调用bmpPtr或clsid上的delete时,无论是从此函数还是在解构函数中,程序都将崩溃。我现在正在使用该程序遇到大量内存泄漏,并且没有运行与Valgrind相当的Windows,我假设它是从这里来的。如何成功删除这些对象?我将以贡献程序员的身份相信源代码中的所有答案。如果需要,请留下任何建议以改善我的问题。

2 个答案:

答案 0 :(得分:0)

scaleHeight = Height + (0.1 * Height);

这似乎是尝试解决DPI缩放问题。如果DPI设置为10%,它将起作用,但是通常不是这种情况。您必须通过清单文件使程序对DPI有所了解。使用SetProcessDPIAware快速修复。

请勿将dcmemdc等声明为类成员。这些是GDI句柄(不是GDI +),通常在功能执行期间,可以短时间握住它们。您必须尽快释放它们。

也不需要将其他变量clsid声明为类成员。您可以根据需要将它们声明为班级成员,但是没有任何收获。

如果您有多显示器设置,则还需要SM_XVIRTUALSCREEN/Y才能获得显示器设置的左上角。

//call this once on start up
SetProcessDPIAware();

HDC dc = ::GetDC(0);
int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
int Height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int Width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
HDC memdc = CreateCompatibleDC(dc);
HBITMAP membit = CreateCompatibleBitmap(dc, Width, Height);
HBITMAP bmpContainer = (HBITMAP)SelectObject(memdc, membit);
BitBlt(memdc, 0, 0, Width, Height, dc, x, y, SRCCOPY);

Bitmap* bmpPtr = new Bitmap(membit, NULL);
// or just Bitmap bmp(membit, NULL);

CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
bmpPtr->Save(L"output.jpg", &clsid);

//cleanup:
delete bmpPtr;
SelectObject(memdc, bmpContainer);
DeleteObject(membit);
DeleteDC(memdc);
ReleaseDC(0, dc);

答案 1 :(得分:0)

此问题的解决方案是使用名称空间删除而不是常规删除。切换到该选项可防止在调试过程中触发断点,并已密封了内存泄漏。

aspect='auto'