Page 1 of 1

Can't use a PNG from Resources file (VS2017)

Posted: Sun May 26, 2019 8:26 pm
by wonkey_monkey
Hi,

I've done a lot of Googling and seen a few people having this same problem, but I've been unable to find a solution that works.

The problem is that I can't use a PNG file from my resource.rc file. I followed the instructions here:

https://wiki.wxwidgets.org/Embedding_PN ... ource_file

but it just won't work.

Code: Select all

wxImage::AddHandler(new wxPNGHandler);

	wxBitmap exit(wxT("exit.png"), wxBITMAP_TYPE_PNG); // this works; it loads the external PNG file and I can then use it on my toolbar
	wxBITMAP_PNG(IDB_PNG2); // this doesn't work (I know the bitmap doesn't get assigned anywhere, but it should still work, right?)
I get this error:

Image

even though IDB_PNG2 is most definitely in my resource.rc and resource.h files:

Code: Select all

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Resource.rc
//
#define IDB_PNG2                        105

Code: Select all

/////////////////////////////////////////////////////////////////////////////
//
// PNG
//

IDB_PNG2                RCDATA                     "hand.png"
I added the file originally by using VS's Import, which added it as a PNG resource, then I closed VS and edited the .rc file manually to change it to an RCDATA.

The only thing I've found so far which does work is:

Code: Select all

wxBITMAP_PNG(#105)
but that's inconvenient, to say the least. Even wxBITMAP_PNG(#IDB_PNG2) doesn't work, even thought IDB_PNG2 is #defined as 105.

What can I do to figure out the problem?

PS My program, such as it is, is based on the HelloWorld example

Re: Can't use a PNG from Resources file (VS2017)

Posted: Sun May 26, 2019 10:04 pm
by doublemax
Remove this line:

Code: Select all

#define IDB_PNG2                        105

Re: Can't use a PNG from Resources file (VS2017)

Posted: Sun May 26, 2019 11:16 pm
by wonkey_monkey
Hmm, okay, that works (I thought just removing #include "resource.h" would work, but it seems those definitions are getting included regardless), and I think I sort of see why...

...but doesn't that now have to potential to screw up any other resources I try to create? And what if I want to be able to refer to that resource by name elsewhere?

Is this just "how it has to be", or is a bit of a bug/oversight in wxWidgets?

Re: Can't use a PNG from Resources file (VS2017)

Posted: Mon May 27, 2019 6:02 am
by doublemax
Is this just "how it has to be", or is a bit of a bug/oversight in wxWidgets?
Honestly, i never thought about that - it has always been that way ;)

I think the problem arises if you use the Visual Studio resource editor to add a file. I just add the line manually in the .rc file.

However, this is a user forum. If you want to discuss this with the core developers, please ask on the wx-users group / mailing list: https://groups.google.com/forum/#!forum/wx-users

Re: Can't use a PNG from Resources file (VS2017)

Posted: Mon May 27, 2019 5:03 pm
by wonkey_monkey
I've gone with this as a Visual Studio compatible alternative:

Code: Select all

	hResource = FindResource(nullptr, MAKEINTRESOURCE(IDB_PNG1), L"PNG");
	size_t _size = SizeofResource(nullptr, hResource);
	hMemory = LoadResource(nullptr, hResource);
	LPVOID ptr = LockResource(hMemory);
	wxBitmap my_bitmap = wxBitmap::NewFromPNGData(ptr, _size);

Re: Can't use a PNG from Resources file (VS2017)

Posted: Mon May 27, 2019 7:33 pm
by PB
I am probably too late but are we clear that the issue is the resource ordinal value versus its name, originating from the hackish way (exploiting the resource ID range and memory addressing limitations) of distinguishing the two as it was created long time ago by Microsoft? The hash in wxBITMAP_PNG definition (used as stringizing operator there) may cause some more confusion as it can be used as the first character of the resource name to make it the ordinal value, see my code below.

wxBITMAP_PNG, just as wxBitmap ctor, expects the resource name (see image sample) and not the ordinal value.

If your application is MSW only and you know you will be always using their ordinals (i.e., numbers and not names) and you wish to use the macro, then the solution should be simple, something like this

Code: Select all

#ifdef __WINDOWS__
    #undef wxBITMAP_PNG
    #define wxBITMAP_PNG(resourceOrdinal) wxBitmap(wxString::Format("#%hu", static_cast<uint16_t>(resourceOrdinal)), wxBITMAP_TYPE_PNG_RESOURCE) 
#endif // #ifdef __WINDOWS__
Unfortunately, such code is somewhat brittle and with macro paramaters not having type checking does not protect nicely from silly mistakes such as passing the resource name instead of ordinal. It would be better to have a simple function such as

Code: Select all

wxBitmap PNGFromResource(uint16_t ordinal)
{
    return wxBitmap(wxString::Format("#%hu", ordinal), wxBITMAP_TYPE_PNG_RESOURCE);
}

Re: Can't use a PNG from Resources file (VS2017)

Posted: Tue May 28, 2019 10:47 pm
by wonkey_monkey
I'm definitely not clear on what causes the issue! Somewhere, I'm guessing, the name is being unexpectedly converted to the ordinal because of the macro #defined in resource.h - remove or rename the macro and the issue disappears. But it's all macros within macros within macros so who knows what's going on...

Re: Can't use a PNG from Resources file (VS2017)

Posted: Wed May 29, 2019 5:40 am
by PB
wonkey_monkey wrote:
Tue May 28, 2019 10:47 pm
I'm definitely not clear on what causes the issue! Somewhere, I'm guessing, the name is being unexpectedly converted to the ordinal because of the macro #defined in resource.h - remove or rename the macro and the issue disappears.
The way I understand it, an MS Windows resource can be identified (e.g., in ::FindResource()) either by its name which is a string (LPCTSTR) or its ordinal which is an integer value.

There is a "hack" using MAKEINTRESOURCE() which allows treating a passed string (pointer to character array) as an integer. It relies on the fact that MS Windows in the user space cannot address memory in the range between 0 and 64 kB. So if the address of a "name" is in that range then it is treated as an ordinal.

If you have

Code: Select all

#define IDB_PNG2 105
then the resource is accessed by an ordinal (integer with a value 105). If you omit the declaration above then the resource is accessed by a name (string with value "IDB_PNG2").

As you can see in the wxBitmap documentation, wxBitmap constructors and Create() methods take only a wxString, so the the trick with ordinal which uses a string literal address cannot be used. It can be worked around using a wxString with the hash sign as the first character of its value, see my previous post.

Most importantly, I believe that the issue can be easily worked around with wxWidgets, see the code in my previous post.