Windows 7 - GetDIBits()

Asked By Bello on 23-Dec-08 08:03 PM
Basically my question is : how GetDIBits() can change its behavior, when I
change the size of a buffer, whose address is passed to the function ?????


I'm having problems inserting the apporpriate code here

Please refer to my post

http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/80e44801-645a-46a0-8bd9-5ac0cdcaf33a

in the VC++ Language Forum.




Joseph M. Newcomer replied on 26-Dec-08 07:58 PM
Define "buffer".  Define "change size".   Define the behavior you are seeing.

Would it have been too painful to include the contents of the question here, instead of
the link?  I can't read it because it attempts to attack my machine by running a script.
joe
Bello replied on 01-Jan-09 07:34 AM
Program listing :


LRESULT CALLBACK WndProc(HWND, UINT, UINT, LONG);

HINSTANCE ghInstance;

int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow)
{
HWND        hWnd;
WNDCLASS    wndclass;
MSG         msg;

ghInstance = hInstance;

if( !hPrevInstance)
{
wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndclass.lpfnWndProc   = (WNDPROC)WndProc;
wndclass.cbClsExtra    = 0 ;
wndclass.cbWndExtra    = 0 ;
wndclass.hInstance     = hInstance ;
wndclass.hIcon         = NULL;
wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName  = NULL;
wndclass.lpszClassName = _T("GetDIBits");

if( !RegisterClass (&wndclass) ) return FALSE;
}

//	Cria a janela da aplicação

hWnd = CreateWindow(_T("GetDIBits"), _T("GetDIBits"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

//	Loop de mensagens

while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (int)msg.wParam ;
}

/************************************************************************************************************************

WndProc(hWnd, iMessage, wParam, lParam) processa as mensagens da janela da
aplicação.

************************************************************************************************************************/

LRESULT CALLBACK WndProc (HWND hWnd, UINT iMessage, UINT wParam, LONG lParam)

{
int					iScanLines;
BITMAP				bm;
DWORD				dw;
HANDLE				hHeap, hMemDC;
HBITMAP				hBitmap, hBitmap1, hBitmap2;
HDC					hDC;
LPBITMAPINFOHEADER	lp, lpbmi;
LPSTR				lpvBits;


switch( iMessage )
{
case WM_LBUTTONDOWN:

//	Load the bitmap in memory

hBitmap1 = LoadBitmap(ghInstance, MAKEINTRESOURCE(BITMAP1));
GetObject(hBitmap1, sizeof(bm), &bm);

//	Get window's DC and the application process heap

hDC = GetDC(hWnd);
hHeap = GetProcessHeap();

//	Allocate BITMAPINFOHEADER structure in hHeap

lpbmi = (LPBITMAPINFOHEADER)HeapAlloc(hHeap, HEAP_ZERO_MEMORY,
sizeof(BITMAPINFOHEADER));

//	Initialize lpbmi->biSize

lpbmi->biSize = sizeof(BITMAPINFOHEADER);

//	Fill the BITMAPINFOHEADER struct

GetDIBits(hDC, hBitmap1, 0, bm.bmHeight, NULL, (LPBITMAPINFO)lpbmi,
DIB_RGB_COLORS);

//	Notice that lpbmi->biCompression = BI_BITFIELDS, which means that the 2nd
//	call to GetDIBIts() below will include 3 DWORD masks after the
//	BITMAPINFOHEADER structure.

//	Let's then reallocate lpdmi to include these 3 DWORD masks

if( !(lp = HeapReAlloc(hHeap, 0, lpbmi, sizeof(BITMAPINFOHEADER) + 3 *
sizeof(DWORD))) )
{
//	There wasn't enough memory to reallocate

//	Free lpbmi, hDC and hBitmap1

HeapFree(hHeap, 0, lpbmi);
ReleaseDC(hWnd, hDC);
DeleteObject(hBitmap1);
}
else
{
//	lpbmi was reallocated to lp

//	Allocate memory block for the bitmap bits

lpvBits = (LPSTR)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, lp->biSizeImage);

//	Fill lpvBits with DIB's array, obtaining in iScanLines the number of
scan
//	lines copied into lpvBits. If there was an error call GetLastError()

if( !(iScanLines = GetDIBits(hDC, hBitmap1, 0, bm.bmHeight, lpvBits,
(LPBITMAPINFO)lp, DIB_RGB_COLORS)) )
dw = GetLastError();

//	Create a compatible bitmap with hDC

hBitmap2 = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);

//	Obtain hBitmap2's bitmap bits from the DIB created above

SetDIBits(hDC, hBitmap2, 0, bm.bmHeight, lpvBits, (LPBITMAPINFO)lp,
DIB_RGB_COLORS);

//	Create memory DC to help paint the bitmaps into hDC

hMemDC = CreateCompatibleDC(NULL);

//	Draw hBitmap1 in hDC

hBitmap = SelectObject(hMemDC, hBitmap1);
BitBlt(hDC, 10, 10, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);

//	Draw hBitmap2 in hDC

hBitmap1 = SelectObject(hMemDC, hBitmap2);
BitBlt(hDC, 200, 10, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);

//	Select into hDC its original bitmap

hBitmap2 = SelectObject(hDC, hBitmap);

//	Free memory blocks, the DC and the bitmaps.

HeapFree(hHeap, 0, lpvBits);
HeapFree(hHeap, 0, lp);
ReleaseDC(hWnd, hDC);
DeleteObject(hBitmap1);
DeleteObject(hBitmap2);
}
break;

case WM_DESTROY:

PostQuitMessage(0);
break;

default:

return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
return 0L ;
}

As you can verify, the program works fine, by painting 2 bitmaps in the
window's application with a left mouse button click in its client area.

The problems start when you play with the size of lpvBits. For instance, if
you change the size to any number greater than 29000, the 2nd call to
GetDIBits() still returns the correct number of scan lines in the bitmap (??).

If you change lpvBits size to 28000, or any other number below that,
GetDIBits() returns 0, i.e., it fails.

How does GetDIBits() change its behavior with the size of lpvBits, without
knowing it ????
Joseph M. Newcomer replied on 02-Jan-09 03:04 PM
See below...

****
You are clearly using some obsolete template, since in Win32, hPrevInstance is ALWAYS set
to NULL, no exceptions, and therefore testing it is meaningless.  This is Win16 template.
You should start programming in the current operating system.
*****
*****
Try to avoid using commas in declaration lists.  It makes the declarations hard to read.
One variable, one line of declaration should be the rule
*****
****
This is incorrect; it is not a string, and should not be declared as an LPSTR.  It is an
array of unsigned bytes, and should be declared as LPBYTE.
*****
****
There are FAR too many variables declared here.  Ideally, you should have none at all.  A
variable that is required for a given message should not be declared until you are in the
message handler.  Better still, you should be dispatching these out to separate functions,
and using windowsx.h.  Dumping all your code into the WndProc creates an unreadable mess.
****
*****
Of course, most of this code is complete trash, because you are not doing the drawing in
the WM_PAINT handler.  So the image will disappear shortly after being drawn, should
anything happen to cover the window.  All this code should do is set things up so the
WM_PAINT handler will do the drawing, then call InvalidateRect(NULL, TRUE)
*****
*****
Why HeapAlloc?  malloc works fine here.
*****
*****
Is it?  I don't see you checking for this, just blindly going ahead an pretending that
this is the state.
*****
******
See comment below.  NEVER embed assignment statements in if-statements.  There is NO
USEFUL PURPOSE served by this horrible practice!
*****
****
This is an erroneous cast; lpvBits is not supposed to be a char array, but a BYTE array
(LPBYTE).  The values in a bitmap are 8-bit UNSIGNED values, and it is inappropriate to
declare them as signed values.  Why are you pretending that lp->biSizeImage is going to be
meaningful in all cases? You should compute it based on the ((color depth) / 8) * width *
height.  You have not said how 28000 relates to lp->biSizeImage, or even what this value
is, so why should we assume that specific randomly-selected values have any relationship
to what is actually required?
*****
******
WHAT KIND OF TRASH IS THIS???  DO NOT EMBED ASSIGNMENTS IN if-STATEMENTS!!!!  The code is
nearly unreadable.  If you want to store something to iScanLines, YOU WRITE AN ASSIGNMENT
STATMENT THAT DOES THAT.  If you want to test that result, YOU WRITE AN if-STATEMENT THAT
TESTS IT.  Code like this should NEVER appear in a real program!!!!!  It serves no purpose
other than to make the code unreadable.
******
*****
So you check that it is an error, and then go on and continue to execute the code;
furthermore, you do not actually display the value of dw, nor say what it is in your
message.  You just say "it failed".  Examination of that error code would ideally be a
useful indicator of what went wrong.  You have also not said what the size of the bitmap
is, and that means there is nothing useful here to diagnose.  I suggest doing

Unless you are going to tell us what the bitmap size is, that is, the height, width, bit
depth, etc., there's not a lot I can say; it sounds like you are just allocating a buffer
too small to hold the image.  But without any useful information about the image, quoting
numbers like 28000 is a completely pointless exercise.

For an example of why it might fail, since you are calling HeapAlloc for a multipage
object, you are probably getting a page-aligned value to a block of pages.  If you try to
write more bytes into this buffer than have been allocated, it probably detects that the
following page is nonexistent, and therefore it gives an error code indicating that you
have an invalid buffer.  But why would you choose some random value like 28000 when the
only possible values are those which are >= the actual number of bytes required to hold
the bitmap.

*****