LoadString with cchBufferMax == 0
-
Hi, The current Microsoft version of the Win32 documentation says that LoadString, LoadStringW, and LoadStringA will pass back a pointer to a read-only copy of a string resource, if the buffer size passed to the routine is 0. In which universe does this happen? This is what I see:
LoadString(hModule, valid\_string\_id, buf, buflen) returns length of string LoadString(hModule, invalid\_string\_id, buf, buflen) returns 0 LoadString(hModule, valid\_string\_id, buf, 0) returns -1 LoadString(hModule, valid\_string\_id, NULL, buflen) throws an exception LoadString(hModule, valid\_string\_id, NULL, 0) throws an exception
I always look at the source code of Wine when I encounter stuff like this, as a first approximation of what might actually be going on internally, and it would appear that Wine would do the same, except for the last case where it would return -1. I can work around this, no problem. The question is more about the mismatch between the documentation and the behavior. Does anyone have any insight on this? sample code below. You need to attach a resource file with a valid string defined
#include #include #include "test.h" // resource include file
#define ARRAYLENGTH(a) (sizeof(a)/sizeof((a)[0]))
int main (int argc, char **argv)
{
char buf[300];
int len;
HMODULE hModule = GetModuleHandle(NULL);#define TEST_FUNC_CALL(expression) \
buf[0] = '?'; \
buf[1] = 0; \
printf("%s\n", #expression); \
__try { \
len = expression; \
printf("... returned %d; buf: %s\n\n", len, buf); \
} \
__except(EXCEPTION_EXECUTE_HANDLER) { \
printf("... threw an exception\n\n"); \
}TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, ARRAYLENGTH(buf)) ) // valid string ID TEST\_FUNC\_CALL( LoadString(hModule, 9999, buf, ARRAYLENGTH(buf)) ) // invalid ID TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, 0) ) // zero size TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, ARRAYLENGTH(buf)) ) // null buffer TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, 0)
-
Hi, The current Microsoft version of the Win32 documentation says that LoadString, LoadStringW, and LoadStringA will pass back a pointer to a read-only copy of a string resource, if the buffer size passed to the routine is 0. In which universe does this happen? This is what I see:
LoadString(hModule, valid\_string\_id, buf, buflen) returns length of string LoadString(hModule, invalid\_string\_id, buf, buflen) returns 0 LoadString(hModule, valid\_string\_id, buf, 0) returns -1 LoadString(hModule, valid\_string\_id, NULL, buflen) throws an exception LoadString(hModule, valid\_string\_id, NULL, 0) throws an exception
I always look at the source code of Wine when I encounter stuff like this, as a first approximation of what might actually be going on internally, and it would appear that Wine would do the same, except for the last case where it would return -1. I can work around this, no problem. The question is more about the mismatch between the documentation and the behavior. Does anyone have any insight on this? sample code below. You need to attach a resource file with a valid string defined
#include #include #include "test.h" // resource include file
#define ARRAYLENGTH(a) (sizeof(a)/sizeof((a)[0]))
int main (int argc, char **argv)
{
char buf[300];
int len;
HMODULE hModule = GetModuleHandle(NULL);#define TEST_FUNC_CALL(expression) \
buf[0] = '?'; \
buf[1] = 0; \
printf("%s\n", #expression); \
__try { \
len = expression; \
printf("... returned %d; buf: %s\n\n", len, buf); \
} \
__except(EXCEPTION_EXECUTE_HANDLER) { \
printf("... threw an exception\n\n"); \
}TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, ARRAYLENGTH(buf)) ) // valid string ID TEST\_FUNC\_CALL( LoadString(hModule, 9999, buf, ARRAYLENGTH(buf)) ) // invalid ID TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, 0) ) // zero size TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, ARRAYLENGTH(buf)) ) // null buffer TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, 0)
I just tried that and it does indeed return the length of the string, and set the buffer address to point to the string itself. In your call with a buffer length of zero, you need to pass the address of a pointer, where Windows will store the address of the string. Something like:
char* pString;
TEST_FUNC_CALL( LoadString(hModule, IDS_STRING1, (char*)&pString, 0) ) // zero size -
Hi, The current Microsoft version of the Win32 documentation says that LoadString, LoadStringW, and LoadStringA will pass back a pointer to a read-only copy of a string resource, if the buffer size passed to the routine is 0. In which universe does this happen? This is what I see:
LoadString(hModule, valid\_string\_id, buf, buflen) returns length of string LoadString(hModule, invalid\_string\_id, buf, buflen) returns 0 LoadString(hModule, valid\_string\_id, buf, 0) returns -1 LoadString(hModule, valid\_string\_id, NULL, buflen) throws an exception LoadString(hModule, valid\_string\_id, NULL, 0) throws an exception
I always look at the source code of Wine when I encounter stuff like this, as a first approximation of what might actually be going on internally, and it would appear that Wine would do the same, except for the last case where it would return -1. I can work around this, no problem. The question is more about the mismatch between the documentation and the behavior. Does anyone have any insight on this? sample code below. You need to attach a resource file with a valid string defined
#include #include #include "test.h" // resource include file
#define ARRAYLENGTH(a) (sizeof(a)/sizeof((a)[0]))
int main (int argc, char **argv)
{
char buf[300];
int len;
HMODULE hModule = GetModuleHandle(NULL);#define TEST_FUNC_CALL(expression) \
buf[0] = '?'; \
buf[1] = 0; \
printf("%s\n", #expression); \
__try { \
len = expression; \
printf("... returned %d; buf: %s\n\n", len, buf); \
} \
__except(EXCEPTION_EXECUTE_HANDLER) { \
printf("... threw an exception\n\n"); \
}TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, ARRAYLENGTH(buf)) ) // valid string ID TEST\_FUNC\_CALL( LoadString(hModule, 9999, buf, ARRAYLENGTH(buf)) ) // invalid ID TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, 0) ) // zero size TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, ARRAYLENGTH(buf)) ) // null buffer TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, 0)
-
Hi, The current Microsoft version of the Win32 documentation says that LoadString, LoadStringW, and LoadStringA will pass back a pointer to a read-only copy of a string resource, if the buffer size passed to the routine is 0. In which universe does this happen? This is what I see:
LoadString(hModule, valid\_string\_id, buf, buflen) returns length of string LoadString(hModule, invalid\_string\_id, buf, buflen) returns 0 LoadString(hModule, valid\_string\_id, buf, 0) returns -1 LoadString(hModule, valid\_string\_id, NULL, buflen) throws an exception LoadString(hModule, valid\_string\_id, NULL, 0) throws an exception
I always look at the source code of Wine when I encounter stuff like this, as a first approximation of what might actually be going on internally, and it would appear that Wine would do the same, except for the last case where it would return -1. I can work around this, no problem. The question is more about the mismatch between the documentation and the behavior. Does anyone have any insight on this? sample code below. You need to attach a resource file with a valid string defined
#include #include #include "test.h" // resource include file
#define ARRAYLENGTH(a) (sizeof(a)/sizeof((a)[0]))
int main (int argc, char **argv)
{
char buf[300];
int len;
HMODULE hModule = GetModuleHandle(NULL);#define TEST_FUNC_CALL(expression) \
buf[0] = '?'; \
buf[1] = 0; \
printf("%s\n", #expression); \
__try { \
len = expression; \
printf("... returned %d; buf: %s\n\n", len, buf); \
} \
__except(EXCEPTION_EXECUTE_HANDLER) { \
printf("... threw an exception\n\n"); \
}TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, ARRAYLENGTH(buf)) ) // valid string ID TEST\_FUNC\_CALL( LoadString(hModule, 9999, buf, ARRAYLENGTH(buf)) ) // invalid ID TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, 0) ) // zero size TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, ARRAYLENGTH(buf)) ) // null buffer TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, 0)
Solved: receiving a pointer to the string when cchBufferMax == 0 happens in the Unicode universe (LoadStringW), not the MBCS universe (LoadStringA). Duh! Of course. String resource data is Unicode. If I'd looked at Wine's LoadStringW instead of LoadStringA I'd have seen that yesterday. (And in case anyone stumbles on this discussion for info about this feature, note that the string in the resource is likely not null terminated.). I'll post feedback for the documentation. It was an error that the same copy got pasted into both LoadString descriptions. LoadStringA needs to have the part about cchBufferMax == 0 deleted. The -1 return value should also be documented. (Also, LoadStringW does not throw exceptions with a NULL buffer pointer, it returns 0, but that doesn't need to be documented.)
-
Hi, The current Microsoft version of the Win32 documentation says that LoadString, LoadStringW, and LoadStringA will pass back a pointer to a read-only copy of a string resource, if the buffer size passed to the routine is 0. In which universe does this happen? This is what I see:
LoadString(hModule, valid\_string\_id, buf, buflen) returns length of string LoadString(hModule, invalid\_string\_id, buf, buflen) returns 0 LoadString(hModule, valid\_string\_id, buf, 0) returns -1 LoadString(hModule, valid\_string\_id, NULL, buflen) throws an exception LoadString(hModule, valid\_string\_id, NULL, 0) throws an exception
I always look at the source code of Wine when I encounter stuff like this, as a first approximation of what might actually be going on internally, and it would appear that Wine would do the same, except for the last case where it would return -1. I can work around this, no problem. The question is more about the mismatch between the documentation and the behavior. Does anyone have any insight on this? sample code below. You need to attach a resource file with a valid string defined
#include #include #include "test.h" // resource include file
#define ARRAYLENGTH(a) (sizeof(a)/sizeof((a)[0]))
int main (int argc, char **argv)
{
char buf[300];
int len;
HMODULE hModule = GetModuleHandle(NULL);#define TEST_FUNC_CALL(expression) \
buf[0] = '?'; \
buf[1] = 0; \
printf("%s\n", #expression); \
__try { \
len = expression; \
printf("... returned %d; buf: %s\n\n", len, buf); \
} \
__except(EXCEPTION_EXECUTE_HANDLER) { \
printf("... threw an exception\n\n"); \
}TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, ARRAYLENGTH(buf)) ) // valid string ID TEST\_FUNC\_CALL( LoadString(hModule, 9999, buf, ARRAYLENGTH(buf)) ) // invalid ID TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, buf, 0) ) // zero size TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, ARRAYLENGTH(buf)) ) // null buffer TEST\_FUNC\_CALL( LoadString(hModule, IDS\_STRING1, NULL, 0)
-
Solved: receiving a pointer to the string when cchBufferMax == 0 happens in the Unicode universe (LoadStringW), not the MBCS universe (LoadStringA). Duh! Of course. String resource data is Unicode. If I'd looked at Wine's LoadStringW instead of LoadStringA I'd have seen that yesterday. (And in case anyone stumbles on this discussion for info about this feature, note that the string in the resource is likely not null terminated.). I'll post feedback for the documentation. It was an error that the same copy got pasted into both LoadString descriptions. LoadStringA needs to have the part about cchBufferMax == 0 deleted. The -1 return value should also be documented. (Also, LoadStringW does not throw exceptions with a NULL buffer pointer, it returns 0, but that doesn't need to be documented.)