0

I'm trying to take a screenshot of a given window and store the bytes into an array. I've already seen this article

Screen capture of specific window c++

but it can't solve my problem (I think). Here's what I've got so far. The code only works partially, namely the width and height are correct. It fails to get the colors of the pixels (because they are all 0 at the end of the function).

And now I'm stuck because I don't know how to further approach this.

edit note: Although I output it as a .bmp I still need the pixels values in the BYTE array.

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>

void screenshot(BYTE **Pixels, char *Window)
{
    // take a screenshot
    HWND hWnd = FindWindowA(NULL, Window);
    if(!hWnd) return;
    HDC hScreen = GetWindowDC(hWnd);
    if(!hScreen) return;
    RECT rWnd;
    if(!GetWindowRect(hWnd, &rWnd)) return;

    int Width = rWnd.right - rWnd.left;
    int Height = rWnd.bottom - rWnd.top;

    HDC hdcMem = CreateCompatibleDC(hScreen);
    if(!hdcMem) return;
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, Width, Height);
    if(!hBitmap) return;
    HGDIOBJ hOld = SelectObject(hdcMem, hBitmap);
    if(!hOld) return;
    if(!BitBlt(hdcMem, 0, 0, Width, Height, hScreen, 0, 0, SRCCOPY)) return;
    if(!SelectObject(hdcMem, hOld)) return;

    BITMAPINFOHEADER BitmapInfoHeader = {0};
    BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    BitmapInfoHeader.biPlanes = 1;
    BitmapInfoHeader.biBitCount = 32;
    BitmapInfoHeader.biWidth = Width;
    BitmapInfoHeader.biHeight = -Height;
    BitmapInfoHeader.biCompression = BI_RGB;
    BitmapInfoHeader.biSizeImage = 4 * Width * Height;

    if(*Pixels)
    {
        free(*Pixels);
    }
    *Pixels = malloc(sizeof **Pixels * 4 * Width * Height);
    if(!*Pixels) 
    {
        return;
    }

    if(!GetDIBits(hdcMem, hBitmap, 0, Height, *Pixels, (BITMAPINFO*)&BitmapInfoHeader, DIB_RGB_COLORS)) return;
    
    ReleaseDC(GetDesktopWindow(),hScreen);
    DeleteDC(hdcMem);
    DeleteObject(hBitmap);



    // save as a bitmap
    FILE * file = fopen("image.bmp", "wb");
    if(!file) return;
    // file header
    BITMAPFILEHEADER BitmapFileHeader = {0};
    BitmapFileHeader.bfType = 0x4D42;
    BitmapFileHeader.bfOffBits = 40 + 14;
    BitmapFileHeader.bfSize = 40 + 14 + BitmapInfoHeader.biSizeImage;
    fwrite(&BitmapFileHeader.bfType, 2, 1, file);
    fwrite(&BitmapFileHeader.bfSize, 4, 1, file);
    fwrite(&BitmapFileHeader.bfReserved1, 2, 1, file);
    fwrite(&BitmapFileHeader.bfReserved2, 2, 1, file);
    fwrite(&BitmapFileHeader.bfOffBits, 4, 1, file);
    // info header
    fwrite(&BitmapInfoHeader.biSize, 4, 1, file);
    fwrite(&BitmapInfoHeader.biWidth, 4, 1, file);
    fwrite(&BitmapInfoHeader.biHeight, 4, 1, file);
    fwrite(&BitmapInfoHeader.biPlanes, 2, 1, file);
    fwrite(&BitmapInfoHeader.biBitCount, 2, 1, file);
    fwrite(&BitmapInfoHeader.biCompression, 4, 1, file);
    fwrite(&BitmapInfoHeader.biSizeImage, 4, 1, file);
    fwrite(&BitmapInfoHeader.biXPelsPerMeter, 4, 1, file);
    fwrite(&BitmapInfoHeader.biYPelsPerMeter, 4, 1, file);
    fwrite(&BitmapInfoHeader.biClrUsed, 4, 1, file);
    fwrite(&BitmapInfoHeader.biClrImportant, 4, 1, file);
    // pixels
    fwrite(*Pixels, BitmapInfoHeader.biSizeImage, 1, file);
    fclose(file);

}

int main(int argc, char **argv)
{
    BYTE *byte;
    screenshot(&byte, "Rechner");
    printf("hello world\n");
    return 0;
}
tadman
  • 208,517
  • 23
  • 234
  • 262
rphii
  • 209
  • 2
  • 13
  • 2
    You are not doing any error checking. Are `FindWindow()`, `CreateCompatibleDC()`, `CreateCompatibleBitmap()`, and `malloc()` returning non-null for success, or null for failure? Are `BitBlt()` and `GetDiBits()` returning non-zero for success, or zero for failure? – Remy Lebeau Feb 17 '21 at 23:39
  • @RemyLebeau yes every function returns non-zero. – rphii Feb 17 '21 at 23:45
  • After `BitBlt()`, if you display the `HBITMAP` in a UI, or save it to a `.BMP` file, do you see the captured window properly? Have you tried using `PrintWindow()` or `WM_PRINT/CLIENT` to capture the window directly to your `HDC`, instead of using `BitBlt()`? – Remy Lebeau Feb 17 '21 at 23:49
  • no, every pixel is black. I'll edit the question so you can see my function to save it as an image. Bear in mind, I'll need some time to do that. – rphii Feb 17 '21 at 23:50
  • What window are you capturing exactly? Maybe the window in question is using [`SetWindowDisplayAffinity()`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity) (or similar API) to actively prevent screen captures being taken of it: "*This feature enables applications to protect their own onscreen window content from being captured or copied*" – Remy Lebeau Feb 17 '21 at 23:51
  • I'm trying it on the Calculator window... – rphii Feb 18 '21 at 00:01
  • `if(!BitBlt(hdcMem, 0, 0, Width, Height, hScreen, 0, 0, SRCCOPY));` This line here is *very* suspicious. – dialer Feb 18 '21 at 00:50
  • 2
    Also this method won't work on the calculator because the calculator uses hardware accelerated rendering. That means it doesn't draw to a `HBITMAP` through a `HDC` like normal Win32 windows do. You'll have to look into how to take a screenshot through DXGI. Can't remember the details off the top of my head. – dialer Feb 18 '21 at 00:59
  • crap I forgot that return, I should've fixed it now. I tested it again and still a black image. Plus I did not know that so thanks, I'll try to see what you mean – rphii Feb 18 '21 at 01:05
  • @rphii Try to capture a different window, like notepad, it should work (although your code is a lot more cumbersome than it needs to be... The `BITMAPINFOHEADER` and `BITMAPFILEHEADER` structs are the way they are so that you can just dump them in a file as they are... with some minor gotchas) – dialer Feb 18 '21 at 01:10
  • Yes I did that just now and it actually works. Oh man, thank you. Did not know that that was a thing.! – rphii Feb 18 '21 at 01:11
  • I can use this code to get the bitmap data of the notepad, but not the bitmap data of the calculator. As dialer said, because the calculator is not rendered like a traditional win32 window. – Zeus Feb 18 '21 at 01:42

0 Answers0