GetDIBits sets image buffer to all 0's (all black)

1.1k Views Asked by At

Trying to take a screenshot of a window as a bitmap. The code below is creating a properly sized bitmap, but every pixel is black. In other words, GetDIBits is setting imageBuffer to all 0's.

The posted code saves a bitmap for every notepad open and visible on the screen. None of the asserts fail.

The BITMAPFILEHEADER and actual writing to a file is omitted, because the final for loop with asserts shows GetDIBits set imageBuffer to all 0's, so there's no need to examine code after that point.

(In the executable's properties, under Configuration Properties->General, I have Character Set to "Not Set" to avoid unicode necessities.)

#include "stdafx.h"

#include <vector>
#include <sstream>
#include <Windows.h>
#include <iostream>
#include <assert.h>
using namespace std;

BOOL CALLBACK getNotepadWindowsCallback(HWND window, LPARAM notepadWindowsLparam) {
    if (NULL != GetParent(window)) {
        return true;
    }
    if (false == IsWindowVisible(window)) {
        return true;
    }
    char text[1024] = { 0 };
    GetWindowText(window, text, sizeof(text));
    if (NULL == strstr(text, " - Notepad")) {
        return true;
    }
    reinterpret_cast<vector<HWND>*>(notepadWindowsLparam)->push_back(window);
    return true;
}

vector<HWND> getNotepadWindows() {
    vector<HWND> notepadWindows;
    EnumWindows(getNotepadWindowsCallback, reinterpret_cast<LPARAM>(&notepadWindows));
    return notepadWindows;
}

int _tmain(int argc, _TCHAR* argv[]) {
    for (HWND notepadWindow : getNotepadWindows()) {
        HDC notepadWindowDeviceContext = GetDC(notepadWindow);
        assert(NULL != notepadWindowDeviceContext);
        HDC memoryDeviceContext = CreateCompatibleDC(notepadWindowDeviceContext);
        assert(NULL != memoryDeviceContext);

        RECT notepadWindowRectangle;
        if (0 == GetClientRect(notepadWindow, &notepadWindowRectangle)) {
            assert(true == false);
        }
        SIZE notepadWindowSize;
        notepadWindowSize.cx = notepadWindowRectangle.right - notepadWindowRectangle.left + 1;
        notepadWindowSize.cy = notepadWindowRectangle.bottom - notepadWindowRectangle.top + 1;

        HBITMAP memoryBitmap = CreateCompatibleBitmap(notepadWindowDeviceContext, notepadWindowSize.cx, notepadWindowSize.cy);
        assert(NULL != memoryBitmap);
        HBITMAP defaultBitmap = static_cast<HBITMAP>(SelectObject(memoryDeviceContext, memoryBitmap));
        assert(NULL != defaultBitmap);

        assert(TRUE == BitBlt(memoryDeviceContext, 0, 0, notepadWindowSize.cx, notepadWindowSize.cy, notepadWindowDeviceContext, notepadWindowRectangle.left, notepadWindowRectangle.right, SRCCOPY));

        BITMAPINFO bitmapinfo = { 0 };
        bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bitmapinfo.bmiHeader.biWidth = notepadWindowSize.cx;
        bitmapinfo.bmiHeader.biHeight = notepadWindowSize.cy;
        bitmapinfo.bmiHeader.biPlanes = 1;
        bitmapinfo.bmiHeader.biBitCount = 4 * 8;
        bitmapinfo.bmiHeader.biCompression = BI_RGB;
        //bitmapinfo.bmiHeader.biSizeImage, per MSDN, may be set to zero for BI_RGB bitmaps

        int imageBufferSize = notepadWindowSize.cx*notepadWindowSize.cy;
        int* imageBuffer = new(int[imageBufferSize]);
        // doing a memset here to initialize imageBuffer to 0's makes no change - leaving it out makes clear GetDIBits is setting imageBuffer to 0's, because it goes in with random garbage
        assert(NULL != SelectObject(memoryDeviceContext, defaultBitmap)); // this must happen before GetDIBits, per MSDN, so memoryBitmap is not selected into a device context
        int returnValue = GetDIBits(memoryDeviceContext, memoryBitmap, 0, notepadWindowSize.cy, static_cast<LPVOID>(imageBuffer), &bitmapinfo, DIB_RGB_COLORS);
        assert(0 != returnValue);
        cout << "returnValue is " << returnValue << endl; // shows proper number of lines is written
        for (int i = 0; i < imageBufferSize; ++i) {
            assert(0 == imageBuffer[i]);
        }

        DeleteDC(memoryDeviceContext);
        ReleaseDC(NULL, notepadWindowDeviceContext);
    }
}
1

There are 1 best solutions below

0
On

Oops. Helps to give BitBlt notepadWindowRectangle.top instead of notepadWindowRectangle.right for its y1 parameter. Fixing that makes it work.