为什么在执行约一千次后CreateCompatibleBitmap会失败?

4

我在编写一个屏幕截取/录制应用程序。以下是捕获屏幕并保存的代码部分:

width = GetSystemMetrics(SM_CXMAXTRACK)+8;
height = GetSystemMetrics(SM_CYMAXTRACK)-8;

hwindowDC=GetDC(GetDesktopWindow());
hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC,COLORONCOLOR);  

// create a bitmap
hbwindow = CreateCompatibleBitmap( hwindowDC, width, height);
cout << " handle to newly created bitmap: " << hbwindow << "\n";

SelectObject(hwindowCompatibleDC, hbwindow); //copy from hwindowCompatibleDC to hbwindow
StretchBlt( hwindowCompatibleDC, 0,0, width, height, hwindowDC, 0, 0,width,height, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !

src.create(height,width,CV_8UC4);   
src.empty();
GetDIBits(hwindowCompatibleDC,hbwindow,0,height,src.data,(BITMAPINFO *)&bi,DIB_RGB_COLORS); 


DeleteDC(hwindowCompatibleDC); 
DeleteObject(hbwindow);

在执行了大约1000次以上的重复后,我的cout语句会显示新创建的句柄为000000000000000即NULL。在那个点之前,我的应用程序都能正常工作。
我每次都要删除已创建的DC和位图,所以没有内存泄漏。任务管理器还证实了没有内存泄漏。那么到底发生了什么呢?
感谢任何能够提供帮助的人。

2
你应该从你的DC中选择对象,然后删除这些对象,最后再删除DC。 - chris
2
虽然听起来很奇怪,但在删除 DC 和位图之前,请尝试将“旧”位图重新选择回您的 DC(为了绝对的安全措施,先选择旧的背景,然后删除 hbwindow,然后删除兼容的 DC)。此外,在这场灾难的最后,似乎缺少了 ReleaseDC(hWindowDC) - WhozCraig
@JesseGood 记住。删除你创建的东西,释放你获取的东西。他应该释放窗口DC,所以你说得对。 - WhozCraig
@WhozCraig:是的,我刚刚意识到了。谢谢。编辑:是的,关于那个点你说得很好。 - Jesse Good
1
始终检查返回值并在适当时调用 GetLastError - n. m.
1个回答

6

如评论中所述,这两个问题是:1)您没有释放从桌面窗口获得的DC,2)您在删除原始位图之前没有将其选择回兼容性DC中。这两个问题都会导致GDI资源泄漏,从而导致您所描述的症状。

下面是修复后的代码:

width = GetSystemMetrics(SM_CXMAXTRACK)+8;
height = GetSystemMetrics(SM_CYMAXTRACK)-8;

hwindowDC=GetDC(GetDesktopWindow());
hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC,COLORONCOLOR);  

// create a bitmap
hbwindow = CreateCompatibleBitmap( hwindowDC, width, height);
cout << " handle to newly created bitmap: " << hbwindow << "\n";

// SAVE OLD BITMAP
HGDIOBJ hOldBmp = SelectObject(hwindowCompatibleDC, hbwindow); //copy from hwindowCompatibleDC to hbwindow
StretchBlt( hwindowCompatibleDC, 0,0, width, height, hwindowDC, 0, 0,width,height, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !

src.create(height,width,CV_8UC4);   
src.empty();
GetDIBits(hwindowCompatibleDC,hbwindow,0,height,src.data,(BITMAPINFO *)&bi,DIB_RGB_COLORS); 

// RESTORE OLD BITMAP
SelectObject(hwindowCompatibleDC, hOldBmp);
DeleteDC(hwindowCompatibleDC); 
DeleteObject(hbwindow);

// RELEASE WINDOW DC
ReleaseDC(GetDesktopWindow(), hwindowDC);

在编写代码时,你应该进行正确的错误检查(例如调用 GetDCCreateCompatibleDC 等函数可能会失败并返回 NULL)。


你听起来好像知道在说什么,所以我相信这就是答案。但是 ReleaseDC 函数并不只需要一个参数。 - john k
当然,你是对的 :) 已在答案中修复。试图同时做三件事情的危险! - Jonathan Potter
3
@user396483 你做出了明智的选择 =) 记住,在 Windows GDI 工作中,几乎所有的事情都适用于“释放所获得的,删除所创建的”(Release what you Get, and Delete what you Create)(当然,如果适用的话,要恢复你所搞砸的)。此外,对你的回答点赞 +1。 - WhozCraig
分离你所附加的东西;P - SChalice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接