You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Need a way to deal with IDC_ARROW and other INTRESOURCEs #69
IDC_ARROW and other resource ids defined with MAKEINTRESOURCE are represented in the headers as wchar_t*, so they can be used with functions like LoadCursor.
LoadCursor(instance, IDC_ARROW);
In the metadata, these resource ids are being represented as ushort, which is fine, in a way, because in the headers they really are an integer value reinterpreted as an address. But this will put the burden on app developers to deal with the type casting.
I think pywin32 dealt with this by manually converting certain parameters of certain functions, but that seems overly labor intensive and unreliable, to figure out all the possible places where these resources might be used in place of a string. If we can do it, great, but I'm skeptical.
Perhaps a more tractable approach would be to mark these resource id constants as "special" in the metadata, either by giving them an attribute, or by wrapping them in a different type. Either way, this would allow projections to project functions like LoadCursror as-is and do the type conversion from integer to "fake string" in a manner that works best for the projection.
An attribute that says that IDC_ARROW can be cast to wchar_t const* would be very helpful for Rust because then it can be written (without unsafe code) as follows:
I'm confused looking at the metadata. IDC_ARROW is defined as 32512 instead of MAKEINTRESOURCE(32512) as the docs say it is. What exactly did MAKEINTRESOURCE do that the metadata is missing? Do we cast the value32512 as a pointer to pass it into LoadCursor? How does Windows know that a valid string will never be at that precise address?
I too would like to make sure that somehow the experience is good for C#.
As far as I can tell, how the API is supposed to work is that IDC_ARROW is defined as (WCHAR*)(32512), aka, the value 32512 cast as a pointer, and the whole system is built on the assumption that a "real pointer" would have bits set outside the lower 16 - pointers would never look like 0x000000nn.
Then the implementation of LoadCursorW checks the value of the string pointer argument, and if the upper 16 (or 48) bits are all zero, they decide "Ah ha! This is a resource ID, not a string". Technically, I believe the HINSTANCE argument also needs to be NULL for this logic to kick in. LoadCursorA does the same upper 16 bit check, forwarding directly to LoadCursorW if its a resource ID, and if not, converts the string with MultibyteToWideChar and then forwards to LoadCursorW.
To look at it another way, it's almost as if these resources were defined like this:
Also, the OPENFILENAMEW and OPENFILENAMEA structures treat their lpTemplateName field the same way, and I'm not sure how broadly they are used.
There's almost certainly other instances of this in win32, these are just the ones I've stumbled across recently. But the fact that we have an example of this appearing in a struct, and not directly as a function param, means that it's going to be hard, if not impossible, to do anything fancy with tweaking certain function signatures to accomodate this. So we'd need to tag the constants themselves in the metadata so that projections can provide a good experience with minimal fuss.
Ideally (but probably not always possible) is if a projection can define IDC_ARROW as a type that can automagically convert into a string pointer so users don't have to add .into(). (As I like to joke, "C++ is unsafe by default" so we can do that in a frictionless way) Kenny's Rust example is the sort of pattern that provides a good compromise, especially with Rust's emphasis on safety.
if a projection can define IDC_ARROW as a type that can automagically convert into a string pointer
I can imagine this in C#, but it bears trying just to make sure that an implicit conversion operator that converts to a pointer isn't disallowed for some reason.
we have an example of this appearing in a struct, and not directly as a function param
I can't find that above. What is the struct that does this?
It seems like custom attributes on the const would be great so projections can do magic. But an attribute on the function parameters that take these would also allow overloads that take the original short type to be provided so "unsafe" code isn't required.
If LoadCursorA's lpCursorName parameter just had an attribute that told us that, in addition to the default PSTR, it can also accept a ushort (the type of IDC_ARROW and friends) then I could make it work seamlessly in Rust, not requiring the caller to add a scary cast.
I came across this closed issue while trying to work out what to do with these parameters in a projection I am writing. I think the fix does not address the overall issue, only the predefined constants.
Technically, I believe the HINSTANCE argument also needs to be NULL for this logic to kick in.
That's true for predefined resources, but resources can also be loaded by ordinal with a non-null HINSTANCE and MAKEINTRESOURCE.
How can a projection allow user-defined resource identifiers if the host language doesn't permit something like reinterpret_cast?
It seems even worse for TaskDialog, because the documentation implies that the pszIcon parameter cannot be a real (non-null) string pointer:
This parameter must be an integer resource identifier passed to the MAKEINTRESOURCE macro or one of the following predefined values. If this parameter is NULL, no icon will be displayed.
It's not ideal, but an answer exists in the form of undocumented behaviour: passing a stringified ID like "#123" works with most parameters which accept MAKEINTRESOURCE values, including the pszIcon parameter of TaskDialog. This form of stringified ID is described in the documentation for FindResourceW, but the documentation for LoadImage and TaskDialog make no mention of it.
I suppose that if a projection can support PWSTR values which aren't valid pointers, like IDC_ARROW, then it can also implement something like MAKEINTRESOURCE. But there's still nothing in the metadata showing which function parameters safely accept such values, which just leaves the "overly labor intensive and unreliable" solution Ryan mentioned above.
Activity
kennykerr commentedon Dec 10, 2020
An attribute that says that
IDC_ARROWcan be cast towchar_t const*would be very helpful for Rust because then it can be written (without unsafe code) as follows:AArnott commentedon Dec 11, 2020
I'm confused looking at the metadata.
IDC_ARROWis defined as32512instead ofMAKEINTRESOURCE(32512)as the docs say it is. What exactly didMAKEINTRESOURCEdo that the metadata is missing? Do we cast the value32512as a pointer to pass it intoLoadCursor? How does Windows know that a valid string will never be at that precise address?I too would like to make sure that somehow the experience is good for C#.
DefaultRyan commentedon Dec 11, 2020
As far as I can tell, how the API is supposed to work is that
IDC_ARROWis defined as(WCHAR*)(32512), aka, the value 32512 cast as a pointer, and the whole system is built on the assumption that a "real pointer" would have bits set outside the lower 16 - pointers would never look like 0x000000nn.Then the implementation of
LoadCursorWchecks the value of the string pointer argument, and if the upper 16 (or 48) bits are all zero, they decide "Ah ha! This is a resource ID, not a string". Technically, I believe theHINSTANCEargument also needs to beNULLfor this logic to kick in.LoadCursorAdoes the same upper 16 bit check, forwarding directly toLoadCursorWif its a resource ID, and if not, converts the string withMultibyteToWideCharand then forwards toLoadCursorW.To look at it another way, it's almost as if these resources were defined like this:
Most of the other resource loading functions all behave the same way: LoadAccelerators, LoadBitmap, LoadIcon, LoadImage, and LoadMenu.
Also, the OPENFILENAMEW and OPENFILENAMEA structures treat their
lpTemplateNamefield the same way, and I'm not sure how broadly they are used.There's almost certainly other instances of this in win32, these are just the ones I've stumbled across recently. But the fact that we have an example of this appearing in a struct, and not directly as a function param, means that it's going to be hard, if not impossible, to do anything fancy with tweaking certain function signatures to accomodate this. So we'd need to tag the constants themselves in the metadata so that projections can provide a good experience with minimal fuss.
Ideally (but probably not always possible) is if a projection can define
IDC_ARROWas a type that can automagically convert into a string pointer so users don't have to add.into(). (As I like to joke, "C++ is unsafe by default" so we can do that in a frictionless way) Kenny's Rust example is the sort of pattern that provides a good compromise, especially with Rust's emphasis on safety.AArnott commentedon Dec 12, 2020
I can imagine this in C#, but it bears trying just to make sure that an implicit conversion operator that converts to a pointer isn't disallowed for some reason.
I can't find that above. What is the struct that does this?
It seems like custom attributes on the const would be great so projections can do magic. But an attribute on the function parameters that take these would also allow overloads that take the original
shorttype to be provided so "unsafe" code isn't required.DefaultRyan commentedon Dec 12, 2020
I mentioned it here:
kennykerr commentedon Mar 3, 2021
If
LoadCursorA'slpCursorNameparameter just had an attribute that told us that, in addition to the defaultPSTR, it can also accept aushort(the type ofIDC_ARROWand friends) then I could make it work seamlessly in Rust, not requiring the caller to add a scary cast.sotteson1 commentedon Mar 25, 2021
This was fixed a while ago by having them be int constants that are PWSTR objects like this:
Lexikos commentedon Oct 24, 2026
I came across this closed issue while trying to work out what to do with these parameters in a projection I am writing. I think the fix does not address the overall issue, only the predefined constants.
That's true for predefined resources, but resources can also be loaded by ordinal with a non-null HINSTANCE and MAKEINTRESOURCE.
How can a projection allow user-defined resource identifiers if the host language doesn't permit something like reinterpret_cast?
It seems even worse for TaskDialog, because the documentation implies that the pszIcon parameter cannot be a real (non-null) string pointer:
It's not ideal, but an answer exists in the form of undocumented behaviour: passing a stringified ID like
"#123"works with most parameters which accept MAKEINTRESOURCE values, including the pszIcon parameter of TaskDialog. This form of stringified ID is described in the documentation for FindResourceW, but the documentation for LoadImage and TaskDialog make no mention of it.I suppose that if a projection can support PWSTR values which aren't valid pointers, like
IDC_ARROW, then it can also implement something like MAKEINTRESOURCE. But there's still nothing in the metadata showing which function parameters safely accept such values, which just leaves the "overly labor intensive and unreliable" solution Ryan mentioned above.