GetDIBits Returns 在屏幕捕获函数中多次迭代后归零

GetDIBits Returns Zero after Many Iterations In Screen Capture function

我正在使用此代码捕获屏幕和光标。

// ConsoleApplication1.cpp : This file contains the 'main' function. Program execution begins and ends there.

#include <Windows.h>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

using namespace std;
using namespace cv;

HWND hwndDesktop = NULL;
bool flg;
CURSORINFO cursor;
ICONINFOEXW info;
BITMAP bmpCursor;
HDC hwindowDC, hwindowCompatibleDC;
HBITMAP hbwindow;
int height, width, srcheight, srcwidth;
Mat src;
BITMAPINFOHEADER  bi;
RECT windowsize;    

Mat hwnd2mat(HWND hwnd)
{
    src.create(height, width, CV_8UC4);

    int val = 0;
    // use the previously created device context with the bitmap
    HGDIOBJ hPrevBitmap = SelectObject(hwindowCompatibleDC, hbwindow);
    // copy from the window device context to the bitmap device context
    val = StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
    if (val <= 0) {
        DWORD error_ = GetLastError();
        cout << "error " << error_ << endl;
    }
    cursor = { sizeof(cursor) };
    ::GetCursorInfo(&cursor);
    if (cursor.flags == CURSOR_SHOWING and flg) {
        info = { sizeof(info) };
        ::GetIconInfoExW(cursor.hCursor, &info);
        const int x = cursor.ptScreenPos.x - windowsize.left - info.xHotspot;
        const int y = cursor.ptScreenPos.y - windowsize.top - info.yHotspot;
        bmpCursor = { 0 };
        val = ::GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
        val = ::DrawIconEx(hwindowCompatibleDC, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
            0, NULL, DI_NORMAL);
        if (val <= 0) {
            DWORD error_ = GetLastError();
            cout << "error " << error_ << endl;
            flg = false;
        }
    }
    val = GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS);  //copy from hwindowCompatibleDC to hbwindow
    if (val <= 0) {
        DWORD error_ = GetLastError();
        cout << "error " << error_ << endl;
        GdiFlush();
        flg = false;
    }

    SelectObject(hwindowCompatibleDC, hPrevBitmap);

    return src;
}

int main(int argc, char **argv)
{
    flg = true;
    hwndDesktop = GetDesktopWindow();
    namedWindow("output", WINDOW_NORMAL);
    int key = 0;
    hwindowDC = GetDC(hwndDesktop);

    if (hwindowDC == NULL) {
        DWORD error_ = GetLastError();
        cout << "error " << error_ << endl;
    }

    hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
    if (hwindowCompatibleDC == NULL) {
        DWORD error_ = GetLastError();
        cout << "error " << error_ << endl;
    }
    SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);

    GetClientRect(hwndDesktop, &windowsize);
    srcheight = windowsize.bottom;
    srcwidth = windowsize.right;
    height = windowsize.bottom / 1;  //change this to whatever size you want to resize to
    width = windowsize.right / 1;

    hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
    if (hbwindow == NULL) {
        DWORD error_ = GetLastError();
        cout << "error " << error_ << endl;
    }
    bi.biSize = sizeof(BITMAPINFOHEADER);    //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
    bi.biWidth = width;
    bi.biHeight = -height;  //this is the line that makes it draw upside down or not
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    while (key != 27)
    {
        Mat src = hwnd2mat(hwndDesktop);
        // you can do some image processing here
        imshow("output", src);
        key = waitKey(1); // you can change wait time
    }

}

截屏效果很好,但截屏8分30多秒后总是失败。 8 分 30 秒后,GetDIBits 开始变为 return 0 。 我 运行 在 64 位机器上使用 windows 10 专业版。 如果我删除光标捕获代码,这个问题似乎不会出现。

DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);

添加这些行就可以了。 感谢Raymond Chen and IInspectable