C++ WM_Paint的BeginPaint()函数导致的内存丢失
我看了这个网站和其他网站对类似问题的多个回复,虽然我觉得我离得更近了,但我还是不能完全正确地回答。不过,这可能是一个非常愚蠢的问题 所以我过去只会每隔几分钟打一个电话给WndProc案例WM_Paint,所以我没有真正注意到泄漏。现在我添加了一个东西,每秒调用它5次。在那一秒钟里,我的内存使用量增加了约3800k。是的,这引起了注意。。。 代码如下:C++ WM_Paint的BeginPaint()函数导致的内存丢失,c++,winapi,C++,Winapi,我看了这个网站和其他网站对类似问题的多个回复,虽然我觉得我离得更近了,但我还是不能完全正确地回答。不过,这可能是一个非常愚蠢的问题 所以我过去只会每隔几分钟打一个电话给WndProc案例WM_Paint,所以我没有真正注意到泄漏。现在我添加了一个东西,每秒调用它5次。在那一秒钟里,我的内存使用量增加了约3800k。是的,这引起了注意。。。 代码如下: LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
// Other cases omitted since we skip them due to "case WM_Paint:".
case WM_PAINT:
wndProc_Paint(hwnd);
break;
// Other cases omitted since we skip them due to "break;".
}
return 0;
}
除了g_hbmWorkingIndicator段和注释掉的部分之外,这就是我的WM_Paint在被调用5次/秒之前的样子,甚至在我将其放入自己的函数之前——这是一个单独的问题
行HDC HDC=BeginPainthwnd,&ps;是内存被添加的地方,它永远不会被释放。每次我们通过函数时都会发生这种情况。所以现在我试着解决这个问题
阅读类似的问题,我认为我需要释放或删除hdc的DC。我没有,现在仍然没有-是的,我已经阅读了MSDN页面,确定SelectObject如何使用这些工具,所以我尝试了使用和不使用ReleaseDC&DeleteDC&hdc和hdcMem。我还尝试删除hdc和hdcMem的对象。这些都没有任何影响
这一模式产生了影响。它解决了这个问题。但这是一个错误的解决方案,因为尽管我没有让记忆失控,但我确实有一些视觉效果被错误的视觉效果所取代。g_hbmGreenLight通常会显示g_hbmBoard的图形,而g_HBM工作灯会显示绿色g_hbmGreenLight的图形。移动模具;在EndPaint~之后使用或尝试使用SelectObject只会更改哪些对象被哪个错误的图形替换-也可以;返回内存泄漏
编辑:为了完整起见,我在这里包含了ConvertipLimageToHBitMapipImage*图像的代码。这完全有可能是罪魁祸首
HBITMAP ConvertIplImageToHBITMAP(IplImage* pImage)
{
IplImage* image = (IplImage*)pImage;
bool imgConverted = false;
if(pImage->nChannels != 3)
{
IplImage* imageCh3 = cvCreateImage(cvGetSize(pImage), 8, 3);
if(pImage->nChannels==1){cvCvtColor(pImage, imageCh3, CV_GRAY2RGB);}
image = imageCh3;
imgConverted = true;
}
int bpp = image->nChannels * 8;
assert(image->width >= 0 && image->height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));
CvMat dst;
void* dst_ptr = 0;
HBITMAP hbmp = NULL;
unsigned char buffer[sizeof(BITMAPINFO) + 255*sizeof(RGBQUAD)];
BITMAPINFO* bmi = (BITMAPINFO*)buffer;
BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);
ZeroMemory(bmih, sizeof(BITMAPINFOHEADER));
bmih->biSize = sizeof(BITMAPINFOHEADER);
bmih->biWidth = image->width;
bmih->biHeight = image->origin ? abs(image->height) : -abs(image->height);
bmih->biPlanes = 1;
bmih->biBitCount = bpp;
bmih->biCompression = BI_RGB;
if (bpp == 8)
{
RGBQUAD* palette = bmi->bmiColors;
int i;
for (i = 0; i < 256; i++)
{
palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)i;
palette[i].rgbReserved = 0;
}
}
hbmp = CreateDIBSection(NULL, bmi, DIB_RGB_COLORS, &dst_ptr, 0, 0);
cvInitMatHeader(&dst, image->height, image->width, CV_8UC3, dst_ptr, (image->width * image->nChannels + 3) & -4);
cvConvertImage(image, &dst, image->origin ? CV_CVTIMG_FLIP : 0);
if(imgConverted)
{cvReleaseImage(&image);}
return hbmp;
}
所以,所有这些都表明:帮助我,你是我唯一的希望 您正在丢失SelectObjecthdcMem、g_hbmBoard返回的原始HBITMAP,因此在调用DeleteDC之前您没有正确还原它,因此它会泄漏。每次调用SelectObject时都会覆盖hbmOld变量,但在再次覆盖hbmOld之前,不会将当前hbmOld值恢复回dcMem。使用SelectObject时,必须在对HDC进行更改后恢复原始对象 请尝试以下方法:
void wndProc_Paint(HWND hwnd)
{
g_hbmBoard = ConvertIplImageToHBITMAP(targetBoardImg); //OpenCV command
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, g_hbmBoard); // save the original HBITMAP
GetObject(g_hbmBoard, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmGreenLight); // returns g_hbmBoard, no need to save it to hbmpOld
GetObject(g_hbmGreenLight, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 59, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmWorkingLight); // returns g_hbmGreenLight, no need to save it to hbmpOld
GetObject(g_hbmWorkingLight, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 94, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmWorkingIndicator); // returns g_hbmWorkingLight, no need to save it to hbmpOld
GetObject(g_hbmWorkingIndicator, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 129, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld); // restore the original HBITMAP
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
// who owns g_hbmBoard? ConvertIplImageToHBITMAP() creates
// a new HBITMAP, so you need to free it with DeleteObject()
// before calling ConvertIplImageToHBITMAP() again. It would
// be better to create g_hbmBoard one time outside of WM_PAINT,
// recreate g_hbmBoard only when the source image actually changes,
// and then re-use g_hbmBoard as-is inside of WM_PAINT.
// ConvertIplImageToHBITMAP() really does not belong in WM_PAINT...
//
//DeleteObject(g_hbmBoard);
}
或者,使用SaveDC/RestoreDC:
如果有什么区别的话,我会看一下g_hbmBoard=。。。与每个WM_绘画信息。绘制结构、内存中的dc和其他GDI设置的其余部分至少在最初看起来是合理的,尽管我承认除了粗略的检查之外,我并没有仔细观察。你正确地选择了旧的hbm,并删除了你创建的dc-correct。你为什么不把它减少到最低限度,并制作一个我们可以编译和运行的完整程序?雷米的答案是一流的,值得最终选择。无关的,如果你感觉到了挑战,你可以考虑利用从PrimeStutt初始化的无效区域CORDES来精炼你所需的区域,或者即使在所讨论的更新区域不在任何特定的给定操作的BLT矩形中,也完全避免它们。当然,这是假设您只正确地通过这里没有看到的代码使首先需要更新的区域无效。否则就没有真正的胜利。祝你好运。@WhozCraig我只在这里没有看到的代码中使需要更新的区域无效,所以我一定会研究一下。也就是说;尽管雷米给出了极好的回答,内存泄漏仍然无法控制。我想理解并修正这个错误,或者首先问这个问题实际上并没有什么作用。回答得好。直到你回答,我才注意到hbmOld的重复覆盖。太棒了,嗯。。。不幸的是,这些都不起作用。在第一个示例中,正在删除所有hbmOld=HBITMAP。。。你所做的一切?至于g_hbmBoard,这是一个全球性的问题。ConvertIplImageToHBITMAP不创建任何新的HDC-它主要使用OpenCV库命令,不应该在屏幕上绘制任何内容。事实上看起来我根本不需要hbmOld,就像你在第二个中所做的那样。这就是我复制/修改示例得到的结果;\u@Alexander ConvertIplImageToHBITMAP是在每次调用时创建一个新位图,还是只返回一个一次性初始化的位图句柄?如果您使用第一个列表,则在从mem dc中选择第一个后删除这些hbmOld覆盖是正确的。显然,第二种方法因其简单性而受到青睐。我想Remy的意思是如果ConvertIplImageToHBITMAP创建了一个新的HBITMAP…@WhozCraig&Remy-奇怪的是,我忘记了ConvertIplImageToHBITMA P实际上是我代码中的一个函数,而不是OpenCV库中的某个函数。我已经在原始问题中包含了上面的代码,所以不只是我给你答案的道听途说@Alexander在创建新的g_hbmBoard之前删除旧的g_hbmBoard。
void wndProc_Paint(HWND hwnd)
{
g_hbmBoard = ConvertIplImageToHBITMAP(targetBoardImg); //OpenCV command
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, g_hbmBoard); // save the original HBITMAP
GetObject(g_hbmBoard, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmGreenLight); // returns g_hbmBoard, no need to save it to hbmpOld
GetObject(g_hbmGreenLight, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 59, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmWorkingLight); // returns g_hbmGreenLight, no need to save it to hbmpOld
GetObject(g_hbmWorkingLight, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 94, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmWorkingIndicator); // returns g_hbmWorkingLight, no need to save it to hbmpOld
GetObject(g_hbmWorkingIndicator, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 129, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld); // restore the original HBITMAP
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
// who owns g_hbmBoard? ConvertIplImageToHBITMAP() creates
// a new HBITMAP, so you need to free it with DeleteObject()
// before calling ConvertIplImageToHBITMAP() again. It would
// be better to create g_hbmBoard one time outside of WM_PAINT,
// recreate g_hbmBoard only when the source image actually changes,
// and then re-use g_hbmBoard as-is inside of WM_PAINT.
// ConvertIplImageToHBITMAP() really does not belong in WM_PAINT...
//
//DeleteObject(g_hbmBoard);
}
void wndProc_Paint(HWND hwnd)
{
g_hbmBoard = ConvertIplImageToHBITMAP(targetBoardImg); //OpenCV command
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
int iOldState = SaveDC(hdcMem); // save everything the HDC currently has selected
SelectObject(hdcMem, g_hbmBoard);
GetObject(g_hbmBoard, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmGreenLight);
GetObject(g_hbmGreenLight, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 59, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmWorkingLight);
GetObject(g_hbmWorkingLight, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 94, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, g_hbmWorkingIndicator);
GetObject(g_hbmWorkingIndicator, sizeof(bm), &bm);
BitBlt(hdc, screenResW - 129, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
RestoreDC(hdcMem, iOldState); // restore everything the HDC originally had selected
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
// who owns g_hbmBoard? ConvertIplImageToHBITMAP() creates
// a new HBITMAP, so you need to free it with DeleteObject()
// before calling ConvertIplImageToHBITMAP() again. It would
// be better to create g_hbmBoard one time outside of WM_PAINT,
// recreate g_hbmBoard only when the source image actually changes,
// and then re-use g_hbmBoard as-is inside of WM_PAINT...
// ConvertIplImageToHBITMAP() really does not belong in WM_PAINT...
//
//DeleteObject(g_hbmBoard);
}