Bence Polgár
Bence Polgár

Reputation: 41

Capture pixel data from only a specified window

I want the code below to take a screenshot of a specified window only, becuse this way BitBlt is faster. The code below takes a screenshot of a window, specified by the name of window, loads the pixel data in a buffer and then re-draws the picture on your screen just to prove the copying worked.

I'd like to take screenshots of browser windows like Google Chrome, but it doesn't seem to work. It draws a black rectangle on my screen with the correct dimensions of the window.

Seems to be working all the time with the window of Minecraft.

Also worked with the Tor Browser.

I noticed that after querying window info, minecraft's window had no child-windows, but when it didn't work with the others, all of them had multiple child-windows.

#include<iostream>
#include<Windows.h>
#include<vector>

using namespace std;

int main() {
    HWND wnd = FindWindow(NULL, "Minecraft 1.15.1");//Name of window to be screenshoted
    if (!wnd) {
        std::cout << "e1\n";
        std::cin.get();
        return 0;
    }
    WINDOWINFO wi = { 0 };
    wi.cbSize = sizeof(WINDOWINFO);

    GetWindowInfo(wnd, &wi);

    RECT rect;
    GetWindowRect(wnd, &rect);

    int width = wi.rcClient.right - wi.rcClient.left;
    int height = wi.rcClient.bottom - wi.rcClient.top;

    cout << width << height;
    /////Fetch window info^^^^^^^^



    BYTE* ScreenData = new BYTE[4 * width*height];


    // copy screen to bitmap
    HDC     hScreen = GetWindowDC(wnd);
    HDC     hDC = CreateCompatibleDC(hScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, width, height);
    HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
    BitBlt(hDC, 0, 0, width, height, hScreen, 0, 0, SRCCOPY);
    SelectObject(hDC, old_obj);

    BITMAPINFO bmi = { 0 };
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    std::cout << GetDIBits(hDC, hBitmap, 0, 0, NULL, &bmi, DIB_RGB_COLORS)<<endl;
    //std::cout << bmi.bmiHeader.biHeight<< " "<< bmi.bmiHeader.biWidth<<endl;
    BYTE*buffer = new BYTE[4* bmi.bmiHeader.biHeight*bmi.bmiHeader.biWidth];
    bmi.bmiHeader.biHeight *= -1;
    std::cout<<GetDIBits(hDC, hBitmap, 0, bmi.bmiHeader.biHeight, buffer, &bmi, DIB_RGB_COLORS)<<endl;//DIB_PAL_COLORS
    /////Load window pixels into a buffer^^^^^^^^


    POINT p;
    int r = 0;
    int g = 0;
    int b = 0;
    int x = 0;
    int y = 0;
    HDC sc = GetDC(NULL);
    COLORREF color;

    while (true) {
        if ((y*-1) == bmi.bmiHeader.biHeight+1 && x == bmi.bmiHeader.biWidth-1) { break; }
        r = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x)+2];
        g = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x)+1];
        b = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x) ];
        color = RGB(r, g, b);
        SetPixel(sc, x, y, color);

        x++;
        if (x == bmi.bmiHeader.biWidth) {
            y++;
            x = 0;
        }
    }
    /////Prove that the copying was successful and buffer is full^^^^^^^^
    Sleep(5000);
    std::cout << "fin\n";
    std::cin.get();
    return 0;
}

Upvotes: 2

Views: 915

Answers (1)

Adrian McCarthy
Adrian McCarthy

Reputation: 47954

I think the problem is the order you're doing things.

Before you put the bitmap on the clipboard, you should select it out of your memory device context.

Once you put the bitmaps on the clipboard, you no longer own it, so you shouldn't try to delete it.

The best way to do that is probably to move your // clean up section before the // save bitmap to clipboard section and to eliminate your DelectObject(hBitmap) statement. So the tail end of your code should probably be:

BitBlt(hDC, 0, 0, width, height, hScreen, 0, 0, SRCCOPY);

// clean up
SelectObject(hDC, old_obj); // selects hBitmap out of hDC
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);

// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);  // clipboard now owns the bitmap
CloseClipboard();

If you still have a problem after those changes, I would check the return value of the SetClipboardData call. If that's failing, GetLastError may give a clue.

Upvotes: 2

Related Questions