使用 C++ returns 旧数据捕获特定 window

Capturing a specific window using C++ returns old data

我目前正在做一个需要截取特定 window 屏幕截图的项目。 这就是我到目前为止所得到的:

主要功能

int main() {
    
    LPCSTR windowname = "Calculator";
    HWND handle = FindWindowA(NULL, windowname);
    
    while (!handle) {
        std::cout << "Process not found..." << std::endl;
        handle = FindWindowA(NULL, windowname);
        Sleep(100);
    }
    
    Mat img = captureScreenMat(handle);
    resetMat();
    imwrite("test2.jpg", img);
    showInMovedWindow("IMG", img);
    waitKey(0);
    destroyAllWindows();
    
   
    return 0;

}

winCapture

Mat src;

void showInMovedWindow(string winname, Mat img) {
    namedWindow(winname);
    moveWindow(winname, 40, 30);
    imshow(winname, img);
}


BITMAPINFOHEADER createBitmapHeader(int width, int height)
{
    BITMAPINFOHEADER  bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = width;
    bi.biHeight = -height;  
    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;

    return bi;
}
int getHeight(HWND hwnd) {
    
RECT rect;
    GetWindowRect(hwnd, &rect);
    int height = rect.bottom - rect.top;
    return height;
    
}

int getWidth(HWND hwnd) {
    
    RECT rect;
    GetWindowRect(hwnd, &rect);
    int width = rect.right - rect.left;
    return width;
    
}
Mat captureScreenMat(HWND hwnd)
{


    HDC hwindowDC = GetDC(hwnd);
    HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
  
    SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);

    int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
    int width = getWidth(hwnd);
    int height = getHeight(hwnd);

    src.create(height, width, CV_8UC4);
    HBITMAP hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
    BITMAPINFOHEADER bi = createBitmapHeader(width, height);

    
    //DEBUG
    cout << hbwindow << endl;
    cout << hwindowCompatibleDC << endl;
    cout << hwindowDC << endl;


    SelectObject(hwindowCompatibleDC, hbwindow);

    StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, screenx, screeny, width, height, SRCCOPY);  
    GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);            


    DeleteObject(hbwindow);
    DeleteDC(hwindowCompatibleDC);
    DeleteDC(hwindowDC);
    ReleaseDC(NULL, hwindowDC);

    return src;
}

void resetMat() {

    src.release();

}

大多数 windows 使用这种方法工作得很好,但有一些 windows 是第一次工作,我尝试对它们进行 img,但每次我尝试采取同一过程的另一个屏幕截图,它只是给了我我截取的第一个屏幕截图。它仅在重新启动该过程后再次起作用,即使如此,它也仅对一个屏幕截图再次起作用,并且之后的所有屏幕截图都是相同的。 我认为这会是某种内存泄漏,但我正在删除所有对象并释放句柄。 我认为手柄有问题,但我不知道是什么。 我不熟悉 windowsAPI 的工作,希望有人比我更了解。

已修复:进程阻止创建句柄。

当您调用 SelectObject 时,您必须保存之前的 selected 句柄(可从 return 值获得)并且您必须 select 在删除或释放设备上下文。

现在你违反了一堆规则。

  1. 正在删除 selected 到设备上下文中的位图。
  2. 正在删除从 GetDC 获得的 DC。
  3. 在同一句柄上同时调用 DeleteDCReleaseDC
  4. 传递 NULL 作为 ReleaseDC 的第一个参数,这应该与传递给 GetDCHWND 相同。
  5. 删除 DC 而不 select 将原始位图重新放入其中。

这些 bug 会把 window DC 搞得一团糟。如果它是瞬态 DC,系统可能会立即清理你的烂摊子。但是如果 window class 有 CS_OWNDCCS_CLASSDC 标志,你会对 window 造成永久性伤害。这就是为什么您的方法似乎适用于某些 windows 而不是其他人。