Malcolm Crum
Malcolm Crum

Reputation: 4869

Repeated calls to CreateCompatibleBitmap() eventually failure with GetLastError() == 6

I have a program that takes a screenshot every second and calculates the average colour of the screen. However, after about 45min of running, my check if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, nScreenWith, nScreenHeight)) == NULL)

begins returning true. A call to GetLastError() returns 6, though I cannot seem to find any documentation on what this means.

Why would thousands of calls to this function work fine, and then abruptly every call fails?

Here's my entire function:

COLORREF ScreenColourCapture::getScreenColour() {
    // Most of this is adapted from http://www.cplusplus.com/forum/beginner/25138/
    LPBITMAPINFO lpbi = NULL;
    HBITMAP OffscrBmp = NULL;
    HDC OffscrDC = NULL;
    int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
    int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    HDC bitmapDC = CreateCompatibleDC(0);
    HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), nScreenWidth, nScreenHeight);
    SelectObject(bitmapDC, hBmp);
    BitBlt(bitmapDC, 0, 0, nScreenWidth, nScreenHeight, GetDC(0), 0, 0, SRCCOPY);
    if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, nScreenWidth, nScreenHeight)) == NULL) {
        int error = GetLastError();
        logging->error("CreateCompatibleBitmap failed!");
        return 0;
    }
    if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL) {
        logging->error("CreateCompatibleDC failed!");
        return 0;
    }
    HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp);
    BitBlt(OffscrDC, 0, 0, nScreenWidth, nScreenHeight, bitmapDC, 0, 0, SRCCOPY);
    if ((lpbi = (LPBITMAPINFO)(new char[sizeof(BITMAPINFOHEADER)+256 * sizeof(RGBQUAD)])) == NULL)
        return 0;
    ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER));
    lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    SelectObject(OffscrDC, OldBmp);

    // First, call GetDIBits with no pixel array. This way it will populate bitmapinfo for us. Then
    // call it with the pixel array and the now populated lpbi.
    GetDIBits(OffscrDC, OffscrBmp, 0, nScreenHeight, NULL, lpbi, DIB_RGB_COLORS);
    LPVOID lpvBits = new char[lpbi->bmiHeader.biSizeImage];
    GetDIBits(OffscrDC, OffscrBmp, 0, nScreenHeight, lpvBits, lpbi, DIB_RGB_COLORS);

    // Pass BMP data off for computation
    COLORREF averageColour = getMeanColourFromPixels((BYTE *)lpvBits, lpbi);

    // Wrap things up
    delete[] lpvBits;
    delete[] lpbi;
    DeleteObject(hBmp);
    DeleteObject(OldBmp);
    DeleteObject(OffscrBmp);
    ReleaseDC(GetDesktopWindow(), bitmapDC);
    DeleteDC(OffscrDC);

    return averageColour;
}

The rest of my code is here, for what it's worth: https://github.com/crummy/screenglow/tree/master/ConsoleApplication1

Upvotes: 3

Views: 1338

Answers (1)

Igor Tandetnik
Igor Tandetnik

Reputation: 52471

You must de-select the bitmap from HDC before you can destroy it, otherwise you are leaking it. It goes like this:

HBITMAP hBmp = CreateCompatibleBitmap(...);
HBITMAP previousBitmap = (HBITMAP)SelectObject(bitmapDC, hBmp);
// ...
SelectObject(bitmapDC, previousBitmap);
DeleteObject(hBmp);

Also, you call GetDC(0) but don't save the return value so you can't ReleaseDC it. Also, bitmapDC is created by CreateCompatibleDC, and should be deleted with DeleteObject, not ReleaseDC.

Upvotes: 10

Related Questions