1

I was attempting to capture a screenshot with C++ (similar to using alt + PrintScreen) by capturing all bits and storing them in a .bmp file. I tested it on many windows/applications, and it occasionally provides the proper result (e.g. File Explorer, Task Manager). However, it occasionally fails to record anything and instead displays a screen with the toolbar at the top and black pixels in the entire bottom (e.g. Outlook, Google Chrome). I'd want to know how to catch the windows that it didn't capture. Here is my code

#include <stdio.h>
#include <windows.h>
using namespace std;

int main()
{
    HWND window = FindWindowA(NULL, "Steam");
    if (window)
    {
        printf("Found the window!\n");

        // Getting window Rect
        RECT rect;
        int window_width, window_height;
        GetWindowRect(window, &rect);
        window_width = rect.right - rect.left;
        window_height = rect.bottom - rect.top;
        printf("Window Size: %d %d\n", window_width, window_height);

        // Getting screen pixels rgba
        BYTE* bitPointer;
        HDC hdcScreen = GetWindowDC(window);
        HDC dc = CreateCompatibleDC(hdcScreen);
        HBITMAP hBitmap = CreateCompatibleBitmap(dc, window_width, window_height);
        BITMAPINFO bitmap;
        bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
        bitmap.bmiHeader.biWidth = window_width;
        bitmap.bmiHeader.biHeight = window_height;
        bitmap.bmiHeader.biPlanes = 1;
        bitmap.bmiHeader.biBitCount = 32;
        bitmap.bmiHeader.biCompression = BI_RGB;
        bitmap.bmiHeader.biSizeImage = window_width * 4 * window_height;
        bitmap.bmiHeader.biClrUsed = 0;
        bitmap.bmiHeader.biClrImportant = 0;
        HBITMAP hBitmap2 = CreateDIBSection(dc, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
        SelectObject(dc, hBitmap2);
        BitBlt(dc, 0, 0, window_width, window_height, hdcScreen, 0, 0, SRCCOPY);

        // saving bits into bmp file
        BITMAPFILEHEADER bmfh;
        const char* szPathName = "testfile.bmp";
        FILE* pFile;
        errno_t err;
        err = fopen_s(&pFile, szPathName, "wb");
        int nBitsOffset = sizeof(BITMAPFILEHEADER) + bitmap.bmiHeader.biSize;
        LONG lImageSize = bitmap.bmiHeader.biSizeImage;
        LONG lFileSize = nBitsOffset + lImageSize;
        bmfh.bfType = 'B' + ('M' << 8);
        bmfh.bfOffBits = nBitsOffset;
        bmfh.bfSize = lFileSize;
        bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
        UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile);
        UINT nWrittenInfoHeaderSize = fwrite(&bitmap, 1, sizeof(BITMAPINFOHEADER), pFile);
        UINT nWrittenDIBDataSize = fwrite(bitPointer, 1, lImageSize, pFile);
        fclose(pFile);

        ReleaseDC(NULL, hdcScreen);
        ReleaseDC(NULL, dc);
    }
    return 0;
}
Jabberwocky
  • 45,262
  • 17
  • 54
  • 100
M812_
  • 39
  • 4
  • If I have correctly read your code, you consistently copy from the top left corner of the **screen** instead of from the top left corner of the window. So this code can only work if you first put the window at the top left corner or maximize it. – Serge Ballesta Jan 12 '22 at 09:48
  • I just tested it with the task manager window, and it captures the window accurately no matter where I place it. So, I don't think it matters if it's in the upper left corner. – M812_ Jan 12 '22 at 09:59
  • 2
    You might want to check this: https://stackoverflow.com/questions/30965343/printwindow-could-not-print-google-chrome-window-chrome-widgetwin-1 – Simon Mourier Jan 12 '22 at 10:03
  • A window may consist of many child windows, including toolbars. Use Spy++ (included with Visual Studio) to see the window hierarchy, to see which window you want to use. – edition Jan 12 '22 at 10:03
  • Ok alright, I will check for it. – M812_ Jan 12 '22 at 10:10
  • 2
    Be prepared that this is going to fail for windows that have explicitly requested to not have their contents captured. [`GetWindowDisplayAffinity`](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdisplayaffinity) allows you to identify them. If you don't want to fiddle with `BitBlt`'s, use the system's graphics capture API (see [New Ways to do Screen Capture](https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/) for more info). @edi The presence of child windows makes no difference here. – IInspectable Jan 12 '22 at 11:25
  • 1
    BitBlt copies GDI data, but if the window doesn't use GDI for rendering (increasingly common, especially for games), there will be nothing for BitBlt to copy. – Raymond Chen Jan 12 '22 at 13:13

0 Answers0