2

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;
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • My main aim was to take screenshots of a browser window. I managed to solve my problem with an alternatív browser. This code works with a browser named Maxthon. Other windows are still misteries. If anyone have a clue, feel free to post your answer! – Bence Polgár Mar 30 '20 at 20:43

1 Answers1

2

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.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • What you wrote, helped. It works, sometimes. I really can't wrap my mind around it. Sometimes the contents behind the window get copied, sometimes just a white rectangle (instead of the black), rarely it works with for ex: chrome and firefox, and it seem to be always working with Minecraft's window regardless whether minecraft's window is above all, or behind some. The only case it doesn't work with minecraft is when its minimised, it copies a small black rectangle. The non client are of the window doesn't get captured btw (like the title bar, etc...), I wonder why is that! – Bence Polgár Mar 29 '20 at 20:21
  • Windows has multiple graphics paths now. Modern apps (like current browsers) and many games use DirectX APIs instead of GDI. They used to be arranged in such a way that you could use GDI to grab a screenshot even if the content was painted with the other channel. That may no longer be true. There are also newer security and DRM features that can make it harder to interact with other process's windows. Windows 10 has a new screen capture API for UWP apps. I'm not sure if there's a WinAPI or COM equivalent available to regular C++ applications. – Adrian McCarthy Mar 30 '20 at 18:00