: FormatMessageA needs be used in a different way (original) (raw)
Describe the bug
Windows settings have “user language id” and “system language id”. They can be different. std::system_category().message() returns "???" when they are different.
By default FormatMessage returns a string in “user language”. The Unicode version of FormatMessage works good in all cases. But the ANSI version, like all other ANSI version Windows APIs, would try to convert the result string to a multibyte string based on “system language id”. It goes wrong when the two ids are different. For example, If your user language is Chinese but the system language is English, it can only returns “????”, because you can’t encode a Chinese string in ISO-8859-1.
So if you need to return the error message in std::string type, there are two solutions:
- Pass the system language id to FormatMessageA
- If you use FormatMessageW, you can set the language id to the system language or thread ACP(the user language). Then when you use WideCharToMultiByte, use CP_ACP for the system language and CP_THREAD_CP for the user language.
Command-line test case
int main() { setlocale(LC_ALL, ""); LANGID user_language_id = GetUserDefaultLangID(); WCHAR buf[1024]; int ret = GetUserDefaultLocaleName(buf,sizeof(buf)/sizeof(buf[0])); assert(ret != 0); std::wcout << user_language_id << L" " << buf << "\n"; ret = GetSystemDefaultLocaleName(buf, sizeof(buf) / sizeof(buf[0])); assert(ret != 0); LANGID system_language_id = GetSystemDefaultLangID(); std::wcout << system_language_id << L" " << buf << std::endl;
wil::unique_hfile file_handle(CreateFile(L"D:\\non_exist_file.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
if (file_handle.get() == INVALID_HANDLE_VALUE) {
const auto error_code = GetLastError();
std::string errmsg = std::system_category().message(error_code); //Sometimes the string only contains "???"
}
return 0; }
To compile it:
C:\Temp>cl /EHsc .\repro.cpp
To reproduce the error:
I assume you have a clean Windows installation. Then please create a new user without Administrator privilege. Then login to the user, go to settings and change the display language to something different. Like, Chinese. In such a case, you only changed the user's locale, not the system. Because the user doesn't have the privilege to change system level settings.
Then run the code, use your debugger to inspect the binary content of the error message. Do not print it out. Printing is a different problem. Just look the raw bytes.
Expected behavior
The error message should contain useful information.
STL version
VS 2022 17.0.3
Additional context
Add any other context about the problem here.