CreateDIBSection is throwing, when trying to create cursor on Win - c++

I am trying to create Cursor from png, and CreateDIBSection() is throwing.
Follwoing is the snippet of code:
HDC hdc = GetDC(NULL);
void* lpBits = NULL;
HBITMAP hBitmap;
try
{
hBitmap = CreateDIBSection(
hdc,
(BITMAPINFO*)&bi,
0,
&lpBits,
NULL,
(DWORD)0);
}
ReleaseDC(NULL, hdc);
As CreateDIBSection is throwing, the code to release DC is not getting executed. can you please let me know the possible issue behind this?

You should make structure zeroed out:
ZeroMemory(&bi,sizeof(BITMAPV5HEADER));
Try out this link , it may help you :
http://support.microsoft.com/kb/318876

Related

Where is the memory leak from my hdc/hbitmap?

so I've noticed that part of my code leaks a lot of memory when it's called and I've tried to find out where or why it leaks but I'm at a dead end.
I've tried the Visual Studio 2017 debugger to take snapshots to find out where the leak happens but according to that there aren't any major leaks. I've also tried Deleaker which I got working once which told me I leaked HDC and HBITMAP but couldn't tell me how much memory.
The first function is the GetScreenBmp where the leak might be, but aren't I releasing everything properly? I know I'm not deleting hBitmap but I need to return that. Is that where the memory leak is?
HBITMAP GetScreenBmp(HDC hdc, int screenPositionX, int screenPositionY, int screenSizeX, int screenSizeY) {
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
int nMousePositionX = 0, nMousePositionY = 0;
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hCaptureDC = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, screenSizeX, screenSizeY);
HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
BOOL bOK = BitBlt(hCaptureDC, 0, 0, screenSizeX, screenSizeY, hdc, screenPositionX, screenPositionY, SRCCOPY | CAPTUREBLT);
SelectObject(hCaptureDC, hOld); // always select the previously selected object once done
DeleteObject(hOld);
DeleteDC(hCaptureDC);
return hBitmap;
The second part is this piece of code, which I'm not entirely sure if I'm deleting everything properly.
HDC hdc = GetDC(0);
HBITMAP hBitmap = GetScreenBmp(hdc, currentSplitInformationArray.screenPositionX, currentSplitInformationArray.screenPositionY, currentSplitInformationArray.screenSizeX, currentSplitInformationArray.screenSizeY);
BITMAPINFO MyBMInfo = { 0 };
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if (0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
MessageBox(NULL, "Resource not available\nDo you want to try again?", "Account Details", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
}
// create the bitmap buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
// Better do this here - the original bitmap might have BI_BITFILEDS, which makes it
// necessary to read the color table - you might not want this.
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biHeight = currentSplitInformationArray.screenSizeY * -1;
// get the actual bitmap buffer
if (0 == GetDIBits(hdc, hBitmap, 0, currentSplitInformationArray.screenSizeY, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
MessageBox(NULL, "Resource not available\nDo you want to try again?", "Account Details", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
}
::SendMessage(testingComparison, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
DeleteObject(&MyBMInfo);
DeleteObject(hBitmap);
ReleaseDC(NULL, hdc);
delete[] lpPixels;
I'm sorry in advance if this is something that has been answered before or if the answer is easily googable, but I've been trying for a few hours to fix it.
Okay I found the solution. The STM_SETIMAGE message returns the previous image and you have to handle it yourself. https://learn.microsoft.com/en-us/windows/desktop/Controls/stm-setimage
I should probably learn to better read the documentation next time, sorry for wasting everyone's time with this one.
::SendMessage(testingComparison, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
Fixed it by simply doing
HBITMAP oldBitmap = (HBITMAP)::SendMessage(testingComparison, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
DeleteObject(oldBitmap);
Use tools to track your leaks/allocations (btw. you didn't post how you found the leaks in the first place).
Since you are using visual studio c++ you may use the built-in tools. Basicly a combination of the these 3 lines can get the job done.
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );//to enable, safe to always set somewhere around program startup - on normal exit this will print whatever you leaked
//_CrtDumpMemoryLeaks();//Dumps to see what has already been allocated
//_CrtSetBreakAlloc(#number);//Use this to set breakpoint using the allocation number from heap dump to see where allocation takes place. If allocation happends before this line it will not work.

WinAPI: Correctly copying a HBITMAP to the clipboard

I have some difficulties trying to copy a HBITMAP to the clipboard. My HBITMAP is created from a COLORREF array and I am able to display it correctly. Here is how it is created:
COLORREF* colors = new COLORREF[imageSize[0] * imageSize[1]];
for (int i = 0; i < imageSize[1]; i++) {
for (int j = 0; j < imageSize[0]; j++) {
colors[imageSize[0] * i + j] = RGB(/* ... */);
}
}
// Create bitmap
HBITMAP hBitmap = CreateBitmap(imageSize[0], imageSize[1], 1, 32, (void*)colors);
delete[] colors;
In order to copy my bitmap to the clipboard, I use this small piece of code:
OpenClipboard(hWnd);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
When I execute my app, I am able to copy the bitmap and paste it somewhere, for example in MS Paint. But if I try to copy it a second time, the clipboard content can't be pasted anymore unless the first piece of code above is executed again.
In the MSDN documentation, it is said that
If SetClipboardData succeeds, the system owns the object identified by the hMem parameter.
I don't understand exactly what this means, but I guess it is the source of my problem. I found an example of a function which does what I want here, but it does not seem to use the same kind of variables. Another example, using strings this time, can be found here.
I am not too sure how to translate this last example to my case. Could you point me in the right direction?
A comment that was deleted helped me find the answer. I actually have to copy my HBITMAP to another HBITMAP before calling SetClipboardData. This way, the copied bitmap can be sent to the clipboard, and the original bitmap is kept for later.
To copy the bitmap, I used the code that can be found in Copying a Bitmap to another Bitmap. In my code, it looks like this:
// Create a new bitmap
HBITMAP hBitmap_copy = CreateBitmap(imageSize[0], imageSize[1], 1, 32, NULL);
// Copy the source bitmap to the new one
HDC srcDC = CreateCompatibleDC(GetDC(NULL));
HDC newDC = CreateCompatibleDC(GetDC(NULL));
HBITMAP srcBitmap = (HBITMAP)SelectObject(srcDC, hBitmap);
HBITMAP newBitmap = (HBITMAP)SelectObject(newDC, hBitmap_copy);
BitBlt(newDC, 0, 0, imageSize[0], imageSize[1], srcDC, 0, 0, SRCCOPY);
SelectObject(srcDC, srcBitmap);
SelectObject(newDC, newBitmap);
DeleteDC(srcDC);
DeleteDC(newDC);
// hBitmap_copy can now be copied to the clipboard
OpenClipboard(hWnd);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap_copy);
CloseClipboard();
I can now copy the displayed bitmap as many times as I want!
// You can't pass hBitmap to SetClipboardData directly
OpenClipboard(NULL)
HBITMAP hBitmap = getBit(); // From somewhere
DIBSECTION ds;
::GetObject(hBitmap, sizeof(DIBSECTION), &ds);
//make sure compression is BI_RGB
ds.dsBmih.biCompression = BI_RGB;
HDC hdc = ::GetDC(NULL);
HBITMAP hbitmap_ddb = ::CreateDIBitmap(
hdc, &ds.dsBmih, CBM_INIT, ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
::ReleaseDC(NULL, hdc);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbitmap_ddb);
CloseClipboard();

Why does GetObject return an BITMAP with null bmBits?

Context: I'm trying to take a screenshot of another window to feed it into OpenCV. I found some code on the web that should be able to convert a BITMAP to something OpenCV can work with. Unfortunately I ran into some trouble.
Question: Why is the bmBits attribute/member always null? (I also tried with PrintWindow instead of BitBlt the result was the same)
#include <iostream>
#include <string>
#include <Windows.h>
int main(int argc, char* argv[])
{
std::wstring windowName = L"Calculator";
RECT rect;
HWND hwnd = FindWindow(NULL, windowName.c_str());
if (hwnd == NULL)
{
return 0;
}
GetClientRect(hwnd, &rect);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
rect.right - rect.left, rect.bottom - rect.top);
SelectObject(hdc, hbmp);
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
BITMAP bmp;
GetObject(hbmp, sizeof(BITMAP), &bmp);
return 0;
}
The bmBits member is non-null for DIB sections. For device-dependent bitmaps (such as the one you're creating), the bmBits is not set because the pixels are on the video card, not in main memory.
In your example, you need to change CreateCompatibleBitmap to CreateDIBSection if you want direct access to the bits.
Just for information.
When loading bitmap from file and want to use BITMAP .bmBits (for glTexImage2D, glDrawPixels):
LoadImage(NULL, "path_to.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE);
u must specify flag LR_CREATEDIBSECTION
HBITMAP hBmp = NULL;
BITMAP BMp;
hBmp = (HBITMAP) LoadImage(NULL, "bitmap.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
GetObject(hBmp, sizeof(BMp), &BMp);
//BMp.bmBits now points to data
From GetObject documentation on MSDN. Please note the second paragraph.
If hgdiobj is a handle to a bitmap created by calling CreateDIBSection, and the specified buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member of the BITMAP structure contained within the DIBSECTION will contain a pointer to the bitmap's bit values.
If hgdiobj is a handle to a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling the GetDIBits or GetBitmapBits function.
One thing which you could do is to look at the return value of GetObject. If 0 you know something has gone wrong. Something wrong with the parameters of the call.

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.