Win32 LoadImage() from File Error - c++

Sorry if this is duplicate but I can't find answer elsewhere. I am simply trying to load an image during runtime using the LoadImage() function of WINAPI. I receive the error code(8) which indicates that there is not enough storage space(error codes found here).
the file is relatively small(2.5kb) so I wonder if there is problem with my code:
void OnCreate()
{
...
HBITMAP hbmDeck = (HBITMAP)LoadImage(hInstance, L"standard.bmp", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
DWORD err = GetLastError();
HBITMAP hbmT = SelectBitmap(hdc, hbmDeck);
if(!hbmT)
{
MessageBox(NULL, L"Failed to LoadImage - 'hbmDeck'", L"OnCreate()", MB_OK);
PostMessage(hwnd, WM_DESTROY, NULL, NULL);
}
...
}

Yeah, some of the API return error codes are a bit cryptic and don't fit the error.
The file you are trying to load "standard.bmp" is a file on the disk NOT in the resource section right? Well to load a file from the disk, the fist parameter of LoadImage (hInst) has to be NULL and the fuLoad flag needs to include LR_LOADFROMFILE which you correctly have.

Related

Win32 Unable to add custom toolbar icon having transparency

I am using this code to add a toolbar to the window with one button having a custom image:
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME, NULL,WS_CHILD |
TBSTYLE_FLAT|TBSTYLE_AUTOSIZE |TBSTYLE_LIST|CCS_BOTTOM, 0, 0, 0, 0,
hwnd, NULL, ghInstance, NULL); //create the toolbar
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0))); //set the font. there cannot be the problem
//↓↓↓↓↓**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
auto hImagelist = ImageList_Create(32, 32,ILC_COLOR32|ILC_MASK,1, 0);
//↑↑↑↑↑**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
HBITMAP bitmap = static_cast<HBITMAP>(ghInstance,
MAKEINTRESOURCE(ID_IMG_SPAWN),// <- ID_IMG_SPAWN is my custom resource
IMAGE_BITMAP, 32, 32, NULL));
ImageList_Add(hImagelist, bitmap, nullptr);
SendMessage(hToolbar, TB_SETIMAGELIST,static_cast<WPARAM>(0),(LPARAM)hImagelist);
In the code above, ID_IMG_SPAWN is the resource bmp image I imported to Visual Studio. However, Visual Studio threw an error saying it didn't recognize my bmp, and the bmp showed up blank when running the application.
Visual Studio told me my bmp wasn't recognized, see the below error:
When the app runs it looks as below:
I learned that Visual Studio hereby recognizes ONLY 24bit bmp.
So, I converted the bmp to 24bit and imported it again, changed ILC_COLOR32 to ILC_COLOR24, that really worked. No error and my image were shown on the button.
However, I lost my alpha channel. Because 24bit bitmap does not support an alpha channel, my image ended up having an ugly square background.
Try something like this (Note I used a "BINARY" resource.. maybe it will work with others too but not sure.. This code works with 32-bit bitmaps, alpha-transparent PNGs, and JPEG):
#include <windows.h>
#include <gdiplus.h>
HBITMAP LoadBitmapFromResource(DWORD ResourceID, bool transparent = true)
{
HANDLE hGlobal = NULL;
ULONG_PTR GDIToken = 0;
Gdiplus::Image* Img = NULL;
Gdiplus::GdiplusStartupInput GDIStartInput = NULL;
Gdiplus::GdiplusStartup(&GDIToken, &GDIStartInput, NULL);
HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(ResourceID), "BINARY");
if (!hResource) {return NULL;}
HGLOBAL hFileResource = LoadResource(NULL, hResource);
if (!hFileResource) {return NULL;}
LPVOID lpFile = LockResource(hFileResource);
if (!lpFile) {return NULL;}
DWORD dwSize = SizeofResource(NULL, hResource);
if (!dwSize) {return NULL;}
void* data = LockResource(hFileResource);
if (!data) {return NULL;}
IStream* pStream = NULL;
hGlobal = GlobalAlloc(GMEM_FIXED, dwSize);
memcpy(hGlobal, data, dwSize);
UnlockResource(hFileResource);
FreeResource(hFileResource);
if (CreateStreamOnHGlobal(hGlobal, true, &pStream) == S_OK)
{
Img = new Gdiplus::Image(pStream, false);
pStream->Release();
GlobalFree(hGlobal);
hGlobal = NULL;
HBITMAP hBitmap = NULL;
static_cast<Gdiplus::Bitmap*>(Img)->GetHBITMAP(transparent ? Gdiplus::Color::Transparent : Gdiplus::Color(0, 0, 0), &hBitmap);
delete Img;
Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
GDIStartInput = NULL;
GDIToken = 0;
return hBitmap;
}
GlobalFree(hGlobal);
hGlobal = NULL;
Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
GDIStartInput = NULL;
GDIToken = 0;
return NULL;
}
You will have to link against GDIPlus. It's not very nice to constantly startup and shutdown GDIPlus for every image you load.. so it's best to move that somewhere.. but other than that, everything should work just fine.
There are other ways without GDIPlus but it makes for a long post..
I learned that Visual Studio hereby recognizes ONLY 24bit bmp.
No, that's wrong. Visual Studio supports alpha-premultiplied 32-bit bitmaps.
32-bit bitmap has 8 bits for ALPHA, it's capable to make a smooth effect.
To show a 32-bit bitmap properly, you have to:
specify ILC_COLOR32
change the resource of ID_IMG_SPAWN to a 32-bit bitmap with premultiplied alpha.
create a DIB section to your bitmap when loading
(Win32's format requirement is very strict)
Q: How to create a DIB section to the bitmap?
A: Specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Explanation:
LoadImage((HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,32, 32,NULL)
This is your code of the LoadImage function. See the MSDN document of LoadImage, to create the DIB section, all you need is to specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Q: How to get a BMP with premultiplied alpha?
A: Pixelformer can help you convert your alpha-channeled file to a premultiplied-alpha BMP.
The steps are
Open your image (any format) in Pixelformer and choose Export from the menu
Select A8:R8:G8: B8 (32bpp) and Premultiplied Alpha, then click Ok.
Then you can save your BMP file! Import this BMP file to Visual Studio resources, replacing your previous 24-bit BMP.
Then, you don't need to use the ImageList_AddMasked (which makes the image sharp) anymore, because you already have a recognizable ALPHA in your 32-bit BMP. So, straight use ImageList_Add.
Okay, after the manipulations explained above your code should be this:
// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,NULL,
WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
0, 0, 0, 0, hwnd, NULL, ghInstance, NULL);
// Set the font (this cannot be the problem)
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0)));
auto hImagelist =
ImageList_Create(32, 32,ILC_COLOR32 /*DON'T NEED THE MASK. CHANGED TO ILC_COLOR32.*/, 1, 0);
HBITMAP bitmap = static_cast<HBITMAP>(LoadImage((HINSTANCE)GetWindowLong(hwnd,
GWL_HINSTANCE), MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,
32, 32, LR_CREATEDIBSECTION /*THIS IS IMPORTANT*/ ));
ImageList_Add(hImagelist, bitmap, NULL);
SendMessage(hToolbar, TB_SETIMAGELIST, static_cast<WPARAM>(0), (LPARAM)hImagelist);
This worked fine as below.
These I answered above is well enough to solve this problem. You may wonder "What means DIB bitmaps?" "What is Premultiplied Alpha?". Those are the deep topics.
To learn DIB bitmaps and Premultiplied Alpha, see the links.
If you are using Visual Studio 2010 or higher (i guess), you can use PNG files. As stated here.

Loading a bmp from resource

I'm trying to load a bitmap from resource instead of a file location. I'm using visual stuidos and I have imported the same bitmap into the rc. The LoadImage returns a NULL when I try with the resource version. Is my syntax wrong? or am I missing additional steps? Please point me in the right direction I'm trying to learn. Thanks in advance.
HBITMAP mhbitmap;
// Loading from a file works
mhbitmap = (HBITMAP)LoadImage(NULL, L"sblue.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
mpiccontrol.SetBitmap(mhbitmap);
if (mhbitmap == NULL) {
MessageBox(L"null", L"from file", NULL);
}
The load from file version shows the image, but the resource version does not show the image.
mpiccontrol.SetBitmap(mhbitmap);
// My attempt at loading from resource
mhbitmap = (HBITMAP)LoadImage(NULL, MAKEINTRESOURCE("IDB_BITMAP1"), IMAGE_BITMAP, 0, 0, 0);
if (mhbitmap == NULL) {
MessageBox(L"null", L"from rc", NULL);
}
mpiccontrol.SetBitmap(mhbitmap);
MAKEINTRESOURCE macro accepts integer resource id so it should be MAKEINTRESOURCE(IDB_BITMAP1) where IDB_BITMAP1 is a resource identifier macro (probably from resource.h). You should also call GetLastError to figure out failure reason.

HBITMAP hbm=LoadImage function returns NULL

I'm having issues displaying a bitmap on the screen. When I complile and run its not displaying anything so I tried debugging step by step and found that the below code is the issue.
HBITMAP hbm = (HBITMAP)LoadImage(hInstance,
"C:\\Users\\Jemma\\Desktop\\Maze Game\\Assets\\TILE_01.bmp",
IMAGE_BITMAP,
SWidth, SHeight,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (hbm == NULL || m_Surface == NULL)
{
DWORD lastError = GetLastError();
return 1;
}
When I get to this function under autos first it says hbm=0xcccccccc{unused=???} (I'm assuming it's due to not being initialised as this comes up when I get to the line so I haven't stepped into it at this point - Just thought I'd put it in just incase) after I've stepped into this line it says hbm = NULL, and the GetLastError function returns 0 which I've read means function was successful. I've tried passing NULL instead of hInstance, I've tried passing the filename in as LPCSTR szFileName -- I declared it as LPCSTR szFileName("C:\Users\Jemma\Desktop\Maze Game\Assets\TILE_01.bmp").
Absolutely no idea what to try next. Any help on this would be greatly appreciated.
First of all, make sure that your image is real bmp file and can be opened with image viewer. Then try this to load that HBITMAP using LoadImage:
const char* filename = "C:\\Users\\Jemma\\Desktop\\Maze Game\\Assets\\TILE_01.bmp";
HBITMAP bmp = (HBITMAP)LoadImage(NULL, filename,
IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
if (hbm == NULL)
{
DWORD lastError = GetLastError();
return 1;
}
Note, you need to use 0, 0 for sizes when you use LR_DEFAULTSIZE, also, when loading from file you need to use LR_LOADFROMFILE. All of these are mentioned in documentation of LoadImage function.
When stepping through with debugger if you get something unexpected you need to check GetLastError. If you are stepping through with VS debugger, you can simply add #err in watch window and you'll always see the last error without changing your code. You can also add it as #err,hr and it will show you readable description of the error that happened.
const char* filename = "C:\\Users\\Jemma\\Desktop\\Maze Game\\Assets\\TILE_01.bmp";
HBITMAP bmp = (HBITMAP)LoadImage(NULL, filename,
IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
if (bmp== NULL)
{
DWORD lastError = GetLastError();
return 1;
}
I am using the same code as above mentioned but LoadImage() is returning NULL and getting lasterror =2 , even i am sure file is already there from belowcode:
string filePath = "D:\\ACC_car_Ego.bmp";
if (boost::filesystem::exists(filePath)) // does filePath actually exist?
DWORD lastError = GetLastError();
else
DWORD lastError1 = GetLastError();

SelectObject returns NULL with hbitmap created in constructor

I have a bitmap class that has a load function for loading the bitmap from either file path or resource ID. This part works fine.
void GtBitmap::Load()
{
LPTSTR szFileName;
szFileName = (LPTSTR)m_strPath.c_str();
// Check for valid .BMP file path
if (m_strPath.size() > 0)
{
// Open .BMP file
m_pFile = fopen(m_strPath.c_str(), ("rb"));
if (m_pFile != NULL)
{
m_hBitmap = (HBITMAP)LoadImage (GetModuleHandle(NULL), szFileName, IMAGE_BITMAP, 0, 0, LR_SHARED | LR_LOADFROMFILE);
GetObject(m_hBitmap, sizeof(m_bmap), &m_bmap);
int i = 1;
}
}
else if (m_intResourceID != 0)
{
m_hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(m_intResourceID), IMAGE_BITMAP, 0, 0, LR_SHARED);
GetObject(m_hBitmap, sizeof(m_bmap), &m_bmap);
int i = 1;
}
}
However, when I try to render it in my code block, the SelectObject returns null. Here is the code for that section of the painter class.
void GtPainterGDI::GtDrawBitmap(GtRectI & target, GtBitmap & bitmap, bool blnOffset)
{
GtCanvas topCv = m_arrCanvas.back();
HDC hdcMem = CreateCompatibleDC(topCv.m_hdcParent);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmap.m_hBitmap);
DWORD lastError = GetLastError();
bool success = BitBlt(hdcMem, target.GetLeft(), target.GetTop(),
target.Width(), target.Height(), hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, bitmap.m_hBitmap);
DeleteDC(hdcMem);
};
The SelectObject() returns null and the image is not drawn. I can only get the image to show up if I use a LoadImage() in that paint function. However I don't want to load the image every time I want to paint. I should be able to load the image once in the Load function or constructor of the bitmap, then use the handle in the paint function.
If anyone could please provide an example of loading an image in a constructor and then painting it elsewhere in the codes WM_PAINT or equivalent painting function I would appreciate it. The code is a new version of the GT graphical user interface library. I plan on posting a new version on codeproject in the next few days or so. I have to do some cleanup first...
Thanks in advance.
HINSTANCE parameter in LoadImage should be NULL when loading the image from file. Use GetModuleHandle(NULL) only when loading from resource.
m_hBitmap = (HBITMAP)LoadImage(NULL, m_strPath.c_str(),
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!m_hBitmap)
{
//report error
}
Also LR_SHARED is not necessary here.
When testing for file's exist, you can use std::ifstream. Example:
#include <fstream>
...
bool test = std::ifstream(m_strPath).good();
This will test for file and close the file handle right away.
Make sure to select hbmOld before deleting hdcMem:
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmap.m_hBitmap);
BitBlt(...)
//SelectObject(hdcMem, bitmap.m_hBitmap); <<= remove this
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);

Win32 application. HBITMAP LoadImage fails to load anything

I'm writing a function that quickly draws an image of a menu for a game I'm making. I'm able to draw the background and text blocks just fine but I'm having trouble creating a bitmap image on the screen
bool menu::drawMenu(PAINTSTRUCT ps)
{
HWND hWnd = GetActiveWindow();
HDC hdc = GetDC(hWnd), hdcMem;
//Draw a new background
HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
SelectObject(hdc, blackPen);
SelectObject(hdc, blackBrush);
Rectangle(hdc, 0, 0, 1080, 720);
//insert selection text
TextOut(hdc, 30, 0, L"New Game", 8);
TextOut(hdc, 30, 30, L"Exit Game", 9);
//draw arrow sprite
HBITMAP arrow = (HBITMAP)LoadImage(NULL, L"C:\\Users\\Tim\documents\\visual studio 2013\\Projects\\BoulderBisque\\BoulderBisque\\arrow.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
DWORD lastError = GetLastError();
if (arrow == NULL)
{
TextOut(hdc, 0, 60, L"Image Load Failed", 17);
return false;
}
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem == NULL)
{
TextOut(hdc, 0, 90, L"Memory Creation Failed", 22);
return false;
}
SelectObject(hdcMem, arrow);
BitBlt(hdc, 0, choice * 30, 16, 16, hdcMem, 0, 0, SRCCOPY);
//cleanup
ReleaseDC(hWnd, hdc);
DeleteDC(hdcMem);
return true;
}
As of now arrow is NULL and I get the Load Image Failed textbox.
I am using the relative path of arrow.bmp I also tried using a full path, that didn't work either.
You may have noticed that this function is outside the WndProc. Everything else draws fine.
I tried running it in there too everything but the arrow.bmp loads.
What am I doing wrong to cause arrow.bmp to be NULL? I have other methods I plan to run in similar ways so getting this function to run would really be a big help.
EDIT* Whenever I give the full path name it still fails to load.
Also is this not the appropriate code for SO? This is my first question...
EDIT** The aditional '/'s haven't fixed the issue.
EDIT*** Using GetLastError, I found the error code to be 2, ERROR_FILE_NOT_FOUND
Your early versions of the question checked the return value of LoadImage but then did nothing more. The documentation says this:
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
So, if the function fails, call GetLastError to find out why. When you do so, you obtain the error code ERROR_FILE_NOT_FOUND. Which is pretty conclusive. The filename that you specified does not exist.
Do note that the code in the latest update to the question calls GetLastError unconditionally. That is a mistake, one that is seen all too frequently here on Stack Overflow. The documentation only tells you to call GetLastError when the call to LoadImage fails. If the call to LoadImage succeeds, then the value return by GetLastError is meaningless. Error handling in Win32 is largely handled in the same way as LoadImage does it, but not always. So you have to read the documentation very carefully.
Perhaps instead of
C:\\Users\\Tim\documents\\...
you meant
C:\\Users\\Tim\\documents\\...
OK, now you've got the path right. The call to LoadImage returns NULL, but GetLastError is no longer helpful and returns ERROR_SUCCESS. Which is weird in itself.
I believe that the problem is that your image uses a format that LoadImage does not understand. I took your .bmp file, loaded it in Paint.net and then re-saved it. Once I did that, the re-saved image was loaded successfully.
Rather than trying to load this from a file it would make far more sense to link the image as a resource and load it that way.
I found through experimenting with Gimp, that your .bmp file must have the following attributes in order for it to work:
depth:24bit
"Do not show write color space information" box checked: Gimp export options
If that still doesn't work then try to "unblock" it: File options.