0

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);
    }
}
jester
  • 31
  • 3
  • This code snippet is large. Can you please post [MCVE](http://stackoverflow.com/help/mcve) – Mohit Jain Mar 25 '15 at 09:18
  • You haven't drawn anything to the bitmap, so naturally it's 'blank'. You also shouldn't create a bitmap from a memDC, since you get a black and white image as a result. You should BitBlt from the target window to a DC that has your memBmp selected into it. Once this is done, you can get the bits from your memBmp. Selecting a bitmap into a hdc just means that subsequent writes to that dc will affect that bitmap - it does not mean that an image already in that DC will get copied to the newly selected bitmap - hence, your bitmap is still empty when you try to get its bits. :) – enhzflep Mar 25 '15 at 11:12
  • @enhzflep, I must be confused about something. Doesn't `HDC memoryDeviceContext = CreateCompatibleDC(notepadWindowDeviceContext` prevent getting a black and white image as a result? Aren't I BitBlt'ing from the target window (notepadWindowDeviceContext) to a DC (memoryDeviceContext) with my memoryBitmap selected into it? (This is the assert(TRUE == BitBlt... line I am referring to.) Isn't that what copies the bits from the window DC to the memory bitmap through the memory DC? – jester Mar 25 '15 at 18:37
  • @Jester- yes indeed. Sorry, I was mistaken about the BitBlt not being performed. Glad to see you've solved your problem. – enhzflep Mar 26 '15 at 02:13

1 Answers1

0

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

jester
  • 31
  • 3