当我在Windows程序中调用以下函数时,程序突然终止。
ScanRect()内的每个函数调用都会成功,包括对GetDIBits()的两个调用。第一个调用将lpvBits设置为NULL,使其用有关像素数据的信息填充bmInfo的BITMAPINFOHEADER,报告的值为每像素32位。
一切似乎都成功了,但是程序突然终止了。我在第二次调用GetDIBits()之后插入了Sleep(8192)行,程序在经过8秒后终止。
是什么导致程序终止?
编辑:根据本帖中的建议对原始代码进行修改。运行函数时未检测到错误,但程序仍意外终止。我意识到内存缓冲区的大小是硬编码的,但它比测试中使用的矩形要大得多。这应该不会导致错误。当然,在我找出程序终止的原因后,我会让程序计算必要的缓冲区大小。
VOID ScanRect(int x, int y, int iWidth, int iHeight) // 992, 96, 64, 80
{ HDC hDC = GetDC(NULL);
if (!hDC)
{
cout << "!hDC" << endl; // error handling ...
}
else
{ HBITMAP hBitmap = CreateCompatibleBitmap(hDC, iWidth, iHeight);
if (!hBitmap)
{
cout << "!hBitmap" << endl; // error handling ...
}
else
{ HDC hCDC = CreateCompatibleDC(hDC); // compatible with screen DC
if (!hCDC)
{
cout << "!hCDC" << endl; // error handling ...
}
else
{ HBITMAP hOldBitmap = (HBITMAP) SelectObject(hCDC, hBitmap);
BitBlt(hCDC, 0, 0, iWidth, iHeight, hDC, x, y, SRCCOPY);
BITMAPINFO bmInfo = {0};
bmInfo.bmiHeader.biSize = sizeof(bmInfo.bmiHeader);
if (!GetDIBits(hCDC, hBitmap, 0, iHeight, NULL, &bmInfo, DIB_RGB_COLORS))
{
cout << "!GetDIBits" << endl; // error handling ...
}
else
{ HANDLE hHeap = GetProcessHeap();
LPVOID pMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 65536); // TODO: calculate a proper size based on bmInfo's pixel information ...
if (!pMem)
{
cout << "!pMem" << endl;
}
else
{ int i = GetDIBits(hCDC, hBitmap, 0, iHeight, pMem, &bmInfo, DIB_RGB_COLORS);
cout << "i returned by GetDIBits() " << i << endl;
HeapFree(hHeap, NULL, pMem);
}
}
SelectObject(hCDC, hOldBitmap);
DeleteDC(hCDC);
}
DeleteObject(hBitmap);
}
ReleaseDC(NULL, hDC);
}
}发布于 2021-05-11 17:59:07
第一个GetDIBits返回的biCompression值为BI_BITFIELDS,在调用第二个GetDIBits之前,需要先调用bmInfo.bmiHeader.biCompression = BI_RGB;。根据c++ read pixels with GetDIBits()的说法,将其设置为BI_RGB是必要的,以避免在结构末尾写入额外的3个DWORD。
发布于 2021-05-11 06:08:05
就像@BenVoigt在评论中所说的那样,在销毁拥有它的HBITMAP之前,你需要恢复用SelectObject()替换的旧HDC。
https://docs.microsoft.com/en-us/windows/win32/gdi/operations-on-graphic-objects
这些函数中的每个函数都返回一个标识新对象的句柄。应用程序检索句柄后,必须调用
SelectObject()函数来替换默认对象。但是,应用程序应该保存标识默认对象的句柄,并在不再需要新对象时使用该句柄替换新对象。当应用程序完成对新对象的绘制时,它必须通过调用SelectObject()DeleteObject()函数恢复默认对象,然后通过调用SelectObject()函数删除新对象。无法删除对象会导致严重的性能问题。
此外,您应该以与创建GDI对象相反的顺序释放它们。
而且,不要忘记错误处理。
试试更多像这样的东西:
VOID ScanRect(int x, int y, int iWidth, int iHeight) // 992, 96, 64, 80
{
HDC hDC = GetDC(NULL);
if (!hDC)
{
// error handling ...
}
else
{
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, iWidth, iHeight);
if (!hBitmap)
{
// error handling ...
}
else
{
HDC hCDC = CreateCompatibleDC(hDC); // compatible with screen DC
if (!hCDC)
{
// error handling ...
}
else
{
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hCDC, hBitmap);
BitBlt(hCDC, 0, 0, iWidth, iHeight, hDC, x, y, SRCCOPY);
SelectObject(hCDC, hOldBitmap);
BITMAPINFO bmInfo = {0};
bmInfo.bmiHeader.biSize = sizeof(bmInfo.bmiHeader);
if (!GetDIBits(hCDC, hBitmap, 0, iHeight, NULL, &bmInfo, DIB_RGB_COLORS))
{
// error handling ...
}
else
{
HANDLE hHeap = GetProcessHeap();
LPVOID pMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 65536); // TODO: calculate a proper size based on bmInfo's pixel information ...
if (!pMem)
{
// error handling ...
}
else
{
int i = GetDIBits(hCDC, hBitmap, 0, iHeight, pMem, &bmInfo, DIB_RGB_COLORS);
HeapFree(hHeap, NULL, pMem);
}
}
DeleteDC(hCDC);
}
DeleteObject(hBitmap);
}
ReleaseDC(NULL, hDC);
}
}你真的应该根据位图的实际宽度、高度、像素深度、扫描线填充大小等来计算缓冲区大小,不要使用硬编码的缓冲区大小。尽管在这个特定的示例中,64K对于64x80 32bpp位图来说应该足够大了,但它只会浪费45K未使用的内存。
https://stackoverflow.com/questions/67477538
复制相似问题