A weird error about GetDIBits and device context

247 views Asked by At

A very weird problem I encountered today. Running codes below on VC6 with MFC project and it is black screen, it works perfectly and shows desktop picture if I take out the comment. However these codes are executed in a infinite loop, so I try to reduce memory copy and memory spend like BitBlt and CreateCompatibleBitmap etc. I do not understand how is my program related to these commented codes. Anyone knows what cause the problem and why?

HDC hdcDesktop = ::CreateDC("DISPLAY", NULL, NULL, NULL);
RECT desktopRect;
::GetWindowRect(::GetDesktopWindow(), &desktopRect);
int desktopWidth = desktopRect.right - desktopRect.left;
int desktopHeight = desktopRect.bottom - desktopRect.top;

HBITMAP hBitmap = CreateCompatibleBitmap(hdcDesktop, desktopWidth, desktopHeight);
/*
HDC hdcMemory = CreateCompatibleDC(hdcDesktop);
SelectObject(hdcMemory, hBitmap);
BitBlt(hdcMemory, 0, 0, desktopWidth, desktopHeight, hdcDesktop, 0, 0, SRCCOPY);
*/

BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

GetDIBits(hdcDesktop, hBitmap, 0, 0, NULL, &bitmapInfo, DIB_RGB_COLORS);
BYTE *pData = new BYTE[bitmapInfo.bmiHeader.biSizeImage];
memset(pData, 0, bitmapInfo.bmiHeader.biSizeImage);
GetDIBits(hdcDesktop, hBitmap, 0, bitmapInfo.bmiHeader.biHeight, pData, &bitmapInfo, DIB_RGB_COLORS);

CRect destRect;
GetClientRect(&destRect);
StretchDIBits(::GetDC(m_hWnd), 0, 0, destRect.Width(), destRect.Height(), 0, 0, bitmapInfo.bmiHeader.biWidth, bitmapInfo.bmiHeader.biHeight, 
    pData, &bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
1

There are 1 answers

0
Michaël Roy On

Here's why commenting out the section doesn't work...

HBITMAP hBitmap = CreateCompatibleBitmap(hdcDesktop, desktopWidth, desktopHeight);

HDC hdcMemory = CreateCompatibleDC(hdcDesktop);
SelectObject(hdcMemory, hBitmap);

// BitBlt makes a copy of the desktop here.
BitBlt(hdcMemory, 0, 0, desktopWidth, desktopHeight, hdcDesktop, 0, 0, SRCCOPY);

BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

// the following lines make use of the contents of bitmap in hBitmap
// commenting out the BitBlt would mean the bitmap is uninitialized.
//
GetDIBits(hdcDesktop, hBitmap, 0, 0, NULL, &bitmapInfo, DIB_RGB_COLORS);
BYTE *pData = new BYTE[bitmapInfo.bmiHeader.biSizeImage];
memset(pData, 0, bitmapInfo.bmiHeader.biSizeImage);  // <-- this is unnecessary.

GetDIBits(hdcDesktop, hBitmap, 0, bitmapInfo.bmiHeader.biHeight, pData, &bitmapInfo, DIB_RGB_COLORS);

You can optimize you code by creating a DIBSection, its associated BITMAPINFO and pData buffer at global scope for your app. Its dimensions are valid for quite a bit of time... You'd have to watch out for changes to screen resolution by handling WM_DISPLAYCHANGE messages (https://msdn.microsoft.com/en-us/library/windows/desktop/dd145210(v=vs.85).aspx).

This would save you the repeated call to CreateCompatibleBitmap().

There is no workaround that I can see to using BitBlt() and GetDIBits() for getting the desktop bits, though.