首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对GetDIBits()的调用成功,但程序终止

对GetDIBits()的调用成功,但程序终止
EN

Stack Overflow用户
提问于 2021-05-11 04:49:57
回答 2查看 102关注 0票数 0

当我在Windows程序中调用以下函数时,程序突然终止。

ScanRect()内的每个函数调用都会成功,包括对GetDIBits()的两个调用。第一个调用将lpvBits设置为NULL,使其用有关像素数据的信息填充bmInfoBITMAPINFOHEADER,报告的值为每像素32位。

一切似乎都成功了,但是程序突然终止了。我在第二次调用GetDIBits()之后插入了Sleep(8192)行,程序在经过8秒后终止。

是什么导致程序终止?

编辑:根据本帖中的建议对原始代码进行修改。运行函数时未检测到错误,但程序仍意外终止。我意识到内存缓冲区的大小是硬编码的,但它比测试中使用的矩形要大得多。这应该不会导致错误。当然,在我找出程序终止的原因后,我会让程序计算必要的缓冲区大小。

代码语言:javascript
复制
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);
    }
}
EN

回答 2

Stack Overflow用户

发布于 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。

More details

票数 1
EN

Stack Overflow用户

发布于 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对象相反的顺序释放它们。

而且,不要忘记错误处理。

试试更多像这样的东西:

代码语言:javascript
复制
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未使用的内存。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67477538

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档