Home > Software design >  Capturing a specific window using C returns old data
Capturing a specific window using C returns old data

Time:01-17

I'm currently working on a project that requires to take a screenshot a specific window. That's what I got so far:

Main function

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();

}

Most windows work really well with this approach, but there are some windows that are working the first time, I try to take a img of them, but every time I try to take another screenshot of the same process, it just gives me the first screenshot I took of it. It only works again after a restart of the process and even then It just works again for one screenshot and all after are the same. I thought it would be some kind of memory leak, but I'm deleting all the objects and releasing the handle. I think that something is wrong with the handle, but I couldn't figure out what. I'm not familiar with working with the windowsAPI and hope someone knowns more than me.

Fixed: Process blocked creating handle.

CodePudding user response:

When you call SelectObject you must save the previous-selected handle (available from the return value) and you MUST select it back before deleting or releasing the device context.

Right now you are breaking a bunch of rules.

  1. Deleting a bitmap which is selected into a device context.
  2. Deleting a DC gotten from GetDC.
  3. Calling both DeleteDC and ReleaseDC on the same handle.
  4. Passing NULL as the first parameter of ReleaseDC, which should be the same HWND passed to GetDC.
  5. Deleting a DC without selecting the original bitmap back into it.

These bugs royally mess up the window DC. If it was a transient DC, the system probably cleans up your mess immediately. But if the window class has the CS_OWNDC or CS_CLASSDC flags, you do permanent damage to the window. That's why your method appears to work with some windows and not others.

  •  Tags:  
  • Related