deleting HBITMAP causes an access violation at runtime - c++

I have the following code to take a screenshot of a window, and get the colour of a specific pixel in it:
void ProcessScreenshot(HWND hwnd){
HDC WinDC;
HDC CopyDC;
HBITMAP hBitmap;
RECT rt;
GetClientRect (hwnd, &rt);
WinDC = GetDC (hwnd);
CopyDC = CreateCompatibleDC (WinDC);
//Create a bitmap compatible with the DC
hBitmap = CreateCompatibleBitmap (WinDC,
rt.right - rt.left, //width
rt.bottom - rt.top);//height
SelectObject (CopyDC, hBitmap);
BitBlt (CopyDC, //destination
0,0,
rt.right - rt.left, //width
rt.bottom - rt.top, //height
WinDC, //source
0, 0,
SRCCOPY);
COLORREF col = ::GetPixel(CopyDC,145,293);
// Do some stuff with the pixel colour....
delete hBitmap;
ReleaseDC(hwnd, WinDC);
ReleaseDC(hwnd, CopyDC);
}
the line 'delete hBitmap;' causes a runtime error: an access violation. I guess I can't just delete it like that?
Because bitmaps take up a lot of space, if I don't get rid of it I will end up with a huge memory leak. My question is: Does releasing the DC the HBITMAP is from deal with this, or does it stick around even after I have released the DC? If the later is the case, how do I correctly get rid of the HBITMAP?

You must destroy GDI objects with DeleteObject, not delete. The latter is only used to free objects allocated using new.

delete should only be used to deallocate things allocated via new.
HBITMAP is a bitmap handle and you need to release the associated object using the GDI function DeleteObject.
Strictly, you should save the result of SelectObject from when you selected the bitmap into the device context and pass that to another call to SelectObject to ensure that the bitmap is not in use by the device context when you call DeleteObject. Things often work if you don't do this, especially if you're about to release the device context anyway but it is safest to do so.

Related

Create a GDI Rectangle image

I must be doing something wrong or have missed something because all I actually want to is render a rectangle into a bitmap, so that I can CreateWindowEx() on it. Does anyone know what I'm missing?
HDC hdc = GetDC(hWnd);
// Create Pen and brush for the rectangle
HPEN pn = CreatePen(style, stroke, pen);
HBRUSH br = CreateSolidBrush(brush);
// Create a compatible bitmap and DC from the window DC with the correct dimensions
HDC bm_hdc = CreateCompatibleDC(hdc);
HBITMAP hImage = CreateCompatibleBitmap(bm_hdc, sz.x, sz.y);
// Select the bitmap, pen, brush into the DC
HGDIOBJ bm_obj = SelectObject(bm_hdc, hImage);
HGDIOBJ pn_obj = SelectObject(bm_hdc, pn);
HGDIOBJ br_obj = SelectObject(bm_hdc, br);
// Draw the rectangle into the compatible DC with the bitmap selected
::Rectangle(bm_hdc, xPos, yPos, xPos + xSize, yPos + ySize);
// Restore the old selections
SelectObject(bm_hdc, br_obj);
SelectObject(bm_hdc, pn_obj);
SelectObject(bm_hdc, bm_obj);
// Delete the not needed DC, pen and brush
DeleteDC(bm_hdc);
DeleteObject(br);
DeleteObject(pn);
ReleaseDC(hWnd, hdc);
// Create the window and send a message to set the static image
HWND win = CreateWindow(TEXT("STATIC"), NULL, WS_CHILD | SS_BITMAP | WS_VISIBLE, pos.x, pos.y, sz.x, sz.y, hWnd, NULL, hInst, NULL)));
HGDIOBJ obj = (HGDIOBJ)SendMessage(win, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);
// Delete the old image
if (obj)
DeleteObject(hImage);
Hummmm... but this doesn't work... All I get is a completely black area and not the rectangle that I have drawn. Any ideas why? Do I need to create another DC and BitBlt() between device contexts?
Thanks for all the help everyone, but I've actually solved it myself and it was SUCH a silly mistake too... Consider this line...:-
::Rectangle(bm_hdc, xPos, yPos, xPos + xSize, yPos + ySize);
Nothing wrong with that at first glance, right? WRONG! If you look at my code, I create a compatible bitmap of the required size to contain my rectangle and try to render the rectangle into this bitmap (which is selected into the DC).
But... WHERE in the bitmap am I rendering? xPos and yPos are window positions of the rectangle, but I'm not rendering to the Window DC am I?!? d'oh! That's right, xPos and yPos should both be 0 because I'm rendering into a bitmap of the correct size and it's when the Window is displayed that xPos and yPos should contain screen coordinates!
Wow... what a dumb mistake and thanks for the nice spot on the HDC from the Window rather than from the compatible DC. I did know that a memory DC has a 1bit depth, but still made that classic blunder. Thanks everyone.
Try changing this line HBITMAP hImage = CreateCompatibleBitmap(bm_hdc, sz.x, sz.y); into this :
HBITMAP hImage = CreateCompatibleBitmap(hdc, sz.x, sz.y);
Paul Watt wrote excellent articles for GDI and image composition with MsImage32.dll.
I am reffering you to this article because it addresses your problem, and here are the relevant quotes and code snippets:
The memory DC is initialized with a mono-chromatic 1x1 pixel bitmap by default.
Avoid a Common Mistake
Before we get too far away from code where I showed you what you need to start running, I want to make sure you are holding this new pair of scissors safely. Do not use a Memory DC with a call to CreateCompatibleBitmap.
...
// You may be tempted to do this; DON'T:
HDC hMemDC = ::CreateCompatibleDC(hDC);
// DON'T DO THIS
// |
// V
HBITMAP hBmp = ::CreateCompatibleBitmap(hMemDC, width, height);
...
// TIP: Try to use the same DC to create
// the Bitmap which you used to create the memory DC.
Remember the part about how The memory DC is initialized with a mono-chromatic 1x1 pixel bitmap by default?!
As for the remarks of member Raymond Chen I believe he is also right, but since you said that your actual code is different this is the only thing I can see as a mistake.
Hopefully this helps.
Best regards.

Drawing bitmap from memory

I need to create the HBITMAP.
Here lies the problem. I have the bmp file's content in memory.
I know how to create a HBITMAP if the bitmap was as a resource.
But since it is in memory, I don't know how to do it.
I do it like this (if in resource) : Link
hDC = BeginPaint(hWnd, &Ps);
// Load the bitmap from the resource
bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_EXERCISING));
// Create a memory device compatible with the above DC variable
MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
SelectObject(MemDCExercising, bmpExercising);
// Copy the bits from the memory DC into the current dc
BitBlt(hDC, 10, 10, 450, 400, MemDCExercising, 0, 0, SRCCOPY);
// Restore the old bitmap
DeleteDC(MemDCExercising);
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
Please guide me on how to do it if it were a memory resource.
Somehow change char img[10000] into a resource ?
Here, img is the memory contraining the contents of the bitmap.
First, let's remove a little innocent gotcha:
hDC = BeginPaint(hWnd, &Ps);
// Load the bitmap from the resource
bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_EXERCISING));
// Create a memory device compatible with the above DC variable
MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
HOBJECT oldbmp = SelectObject(MemDCExercising, bmpExercising); //<<<<save it for later ...
// Copy the bits from the memory DC into the current dc
BitBlt(hDC, 10, 10, 450, 400, MemDCExercising, 0, 0, SRCCOPY);
// Restore the old bitmap
SelectObject(MemDCExercising, oldbmp); //<<<... DeleteDC will leak memory if it holds a resource
DeleteDC(MemDCExercising);
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
Now, an HBITMAP is (conceptually speaking) a pointer to an internal structure that holds a "pointer" (in fact is much more a stream) towards the GDI memory space you cannot access.
A "memory bitmap" is not represented into your program as a memory buffer that belogs to your program, but as ... an HBITMAP obtained with CreateCompatibleBitmap, where the HDC parameter id the DC the bitmap has to be compatible with. (usially a screen, window or painting DC).
You can cerate an initialized bitmap passing a buffer containing initial data, or get the data a bitmap holds with CreateBitmap or GetBitmapBits.
In any case, these are your local copy of the bitmap data, not the "live bitmap" GDI draws in.
Note also that the internal structure of those data depends on the format (how many bits per pixel on how many planes and with or without a palette) the bitmap needs to have, and that, to avoid performance penalty in the Blit process, it must coincide with the format used by your screen setup.
This does not necessarily have to be the same as the one the bitmap has when saved into a "bmp" file.

MSPaint-like app writing. How to do BitBlt right?

I'm writing now simple mspaint-like program in C++ using windows.h (GDI). For my program I need only pen tool. So, I need to store somewhere main window's picture (for ex. in memory HDC and HBITMAP) to draw it after in WM_PAINT message.
When I first have to store window's HDC to my memory HDC and HBITMAP? In what message I should store window? For example, I think we can't do it in WM_CREATE because we have no window yet.
What is the difference between PatBlt and BitBlt? What should I use for my app?
How to copy window's HDC content to my memory HDC and Bitmap? I'm trying to do something like this:
LPRECT lpRect;
GetClientRect(hwnd, lpRect);
width = lpRect->right - lpRect->left;
height = lpRect->bottom - lpRect->top;
HDC hDC = GetDC(hwnd);
memoryDC = CreateCompatibleDC(hDC);
memoryBitmap = CreateCompatibleBitmap(hDC, width, height);
SelectObject(memoryDC, memoryBitmap);
PatBlt(memoryDC, 0, 0, width, height, PATCOPY);
ReleaseDC(hwnd, hDC);
But this don't work: program crashes.
How to restore window in WM_PAINT after that?
How to clear my window with white color?
1: I would recommend you lazy load your off-screen canvas as late as possible. If you need it in WM_PAINT and you haven't created it yet, create it then. If you need it at the point someone begins drawing, create it then. If it exists when you need it, then use it.
2: PatBlt fills a region of a bitmap using the device context's current brush. Brushes define patterns, which is why it's called PatBlt. BitBlt copies data from a source bitmap to a destination bitmap. You would use a BitBlt when you wanted to move the image from the off-screen bitmap to the frame buffer.
3: The lpRect parameter of GetClientRect is an output parameter. That means you have to supply the memory. In this case, GetClientRect is trying to write the rectangle to a null pointer and causing the crash.
RECT clientRect;
GetClientRect(hwnd, &clientRect);
width = clientRect.right - clientRect.left;
height = clientRect.bottom - clientRect.top;
WM_PAINT: seems to be the best place to create the memory hdc. You can do something like this
WM_PAINT:
if (!first_paint)
{
...code
first_paint = true;
}
...more code
break;

Win32 Device Context without Window

In my application i need to create HBITMAP objects to which I render and from where I copy the result.
I use the function "CreateDIBSection" to create these bitmaps, however this function required a DC (Device Context) as first parameter. Currently I get this by calling GetDC(hWnd) on the main windows handle (hWnd). But I would like to be able to create HBITMAPS without the requirement of having an application window, without some kind of in memory DC, Is this possible?
CreateCompatibleDC(NULL) will create you a device context that is compatible with the screen - which sounds like it would be ideal in the situation.
You can get one with CreateDC for the display:
HDC hDc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
Cleanup with DeleteDC(). It is only used to initialize the palette for bitmaps with indexed format. NULL might work if you don't use such a format, never tried it.
Then there's GDI+, #include <gdiplus.h> and the Bitmap class...
try this. it worked.
HDC hdcScreen = ::GetDC( NULL );
HDC hdcMemDC = ::CreateCompatibleDC(hdcScreen);
HBITMAP hbmScreen = ::CreateCompatibleBitmap(hdcScreen, cx, cy);
HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hdcMemDC, hbmScreen);
MyImageDraw(hdcMemDC, ...);
// The drawing image is held in hBitmap. You can save it
HBITMAP hBitmap = (HBITMAP)::SelectObject(hdcMemDC, hOldBitmap);
// save The trend image into c:\test.bmp
PBITMAPINFO pbi = CreateBitmapInfoStruct(hBitmap);
CreateBMPFile("C:\\Temp\\test.bmp", pbi, hBitmap, hdcMemDC);
//Clean up
::DeleteObject(hbmScreen);
::DeleteObject(hdcMemDC);
::ReleaseDC( NULL, hdcScreen );

Strange error with CreateCompatibleDC

Maybe this is a foolish question, I can't see why I can not get a DC created in the following code :
HBITMAP COcrDlg::LoadClippedBitmap(LPCTSTR pathName,UINT maxWidth,UINT maxHeight)
{
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, pathName, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (!hBmp)
return NULL;
HDC hdc = (HDC)GetDC();
HDC hdcMem = CreateCompatibleDC(hdc);
if (!hdcMem)
{
DWORD err = GetLastError();
}
...
...
...
The bitmap hBmp is loaded fine and hdc has a valid value. But the call to CreateCompatibleDC() returns a NULL pointer. Then, GetLastError() returns 0 !
Anybody can guess what's going on here , please ?
PS : There are no memory allocations or GDI routines called before this one...so I think memory leaks should be ruled out.
You are improperly casting the result of GetDC() to an HDC. GetDC() returns a pointer to a CDC object.
To do what you want you can do either of the following. The first choice fits more into how MFC likes to do things, but both work just fine:
CDC *pDC = GetDC();
// Option 1
CDC memDC;
memDC.CreateCompatibleDC(pDC);
// Option 2
HDC hMemDC = CreateCompatibleDC((HDC)(*pDC));
It is important to note that option 2 does not do the same thing that you're currently doing wrong. The CDC class has an operator HDC() member that allows it to be converted to an HDC, but this does NOT apply to the pointer. You must dereference it first.
Certain device contexts won't work with CreateCompatibleDC(). The DC has to support raster operations. You can feed the hdc to GetDeviceCaps() and check RASTERCAPS.
But it turns out the GetDC you're calling is a method on a class and not the C binding I thought you meant. SoapBox has the right answer.