-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Visual Studio 2022 std::system_category
returns "unknown error" if system locale is not en-US
#3254
Comments
This is what happens:
from FormatMessageA doc:
So if language id was 0 as before the mentioned commit, I'll get at least a message in US English if I don't have the messages in my system locale language and this is why it was working before. Now This is the second time I'm hit by locale issues. Please test locale related code on different locales other than "en-US" before shipping code! |
We do. I think at least "ru-Ru" and "zh-CN" are tested. |
#include <clocale>
#include <iostream>
#include <string>
#include <Windows.h>
int main() {
setlocale(LC_ALL, "");
auto file_handle(CreateFile(
L"D:\\non_exist_file.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL));
if (file_handle == INVALID_HANDLE_VALUE) {
const auto error_code = GetLastError();
std::string errmsg = std::system_category().message(error_code);
std::cout << errmsg << '\n';
}
} #include <clocale>
#include <windows.h>
#include <system_error>
#include <stdio.h>
int main()
{
setlocale(LC_ALL, "");
std::error_code ec(ERROR_DIR_NOT_EMPTY, std::system_category());
printf("message = '%s' (%lld)\n", ec.message().c_str(), static_cast<long long>(_MSC_FULL_VER));
} I will investigate what to do if windows installations do not have error messages in the selected language. |
The error set by Can you fallback to language id 0 if retried with language id other than 0 and failed with ERROR_MUI_FILE_NOT_FOUND or ERROR_RESOURCE_LANG_NOT_FOUND ? |
On my computer the Because the interface language is English, I expect |
In #2451 , the user expected that if his interface language is Chinese and system default locale is English the message should be returned in English. And it seems we can't even use English always: #3260 (comment) |
@fsb4000 , in my case:
|
I know it is not back compatible, but I believe during the application initialization on startup the C++ locale must be set to the OS User Language. Later |
I have the same problem, with English (Australian) and other English locales. TBH I expect @dovdiienko-el3 I don't think the C++ locale or specifically @StephanTLavavej Can you escalate the problem to the Windows SDK team on what to do? |
@StephanTLavavej - This is a big issue for any developer whose system locale isn't set to en-US. It was definitely introduced in the VS2022 (v143) platform toolset and doesn't exist in its predecessors. I've done a lot below to help you repro and even fix. I'm sure everyone in this thread would really appreciate if you could bring this to the attention of the relevant team. This min repro program will produce different output with different platform toolsets: #include <system_error>
#include <cstdio>
int main()
{
std::printf("Oops: %s\n", std::system_category().message(2).c_str());
} I've avoided including Windows.h here for brevity but error 2 is
But if built using the VS2022 (v143) platform toolset it produces the following output:
The problem seems to be in your internal function
Note the
Instead of using a hard-coded I think the approach here should be to try progressively more general LANGIDs until a mapping is found. In other words, start with the full LANGID which will consist of a specific primary language and sub-language. If that doesn't find a mapping, drop back to trying the primary language and a default sub-language. If that fails, drop back to primary language and neutral sub-language. Finally, if that fails, drop back to neutral primary language and neutral sub-language, i.e. 0. FWIW, this is how I'm doing it: template <class CharT>
constexpr auto GetFormatMessageFunction()
{
if constexpr (std::is_same_v<CharT, char>)
{
return &FormatMessageA;
}
else
{
return &FormatMessageW;
}
}
template <class CharT = char>
std::basic_string<CharT> GetSystemErrorMesssage(const DWORD errorCode)
{
const auto formatMessageFunction = GetFormatMessageFunction<CharT>();
DWORD langId;
if (0 == GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT,
LOCALE_ILANGUAGE |
LOCALE_RETURN_NUMBER,
reinterpret_cast<LPWSTR>(&langId),
sizeof(langId) / sizeof(wchar_t)))
{
langId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
}
std::basic_string<CharT> result;
for (;;)
{
CharT* msg;
const auto msgLen = formatMessageFunction(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errorCode,
langId,
reinterpret_cast<CharT*>(&msg),
0,
nullptr);
if (msgLen != 0)
{
try
{
result.assign(msg, msgLen);
}
catch (...)
{
LocalFree(msg);
throw;
}
LocalFree(msg);
break;
}
const auto primaryLangId = PRIMARYLANGID(langId);
if (primaryLangId == LANG_NEUTRAL)
{
break;
}
const auto subLangId = SUBLANGID(langId);
if (subLangId == SUBLANG_NEUTRAL)
{
langId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
}
else if (subLangId == SUBLANG_DEFAULT)
{
langId = MAKELANGID(primaryLangId, SUBLANG_NEUTRAL);
}
else
{
langId = MAKELANGID(primaryLangId, SUBLANG_DEFAULT);
}
}
for (auto charIndex = result.size() - 1; charIndex != MAXSIZE_T; charIndex--)
{
if (result[charIndex] != '\n' && result[charIndex] != '\r')
{
break;
}
result.resize(charIndex);
}
return result;
} |
It is hard to believe it is true. This kind of problem must be treated as a show stopper from my point of view and has to be fixed ASAP. In fact 1.5 years passed since this issue had been reported and it is still actual. This issue prevents developers from using the |
I've read through #3260 and FWIW, it seems like it will fix the problem. Do I understand correctly that the workarounds for this issue are:
None of those seem very practical. Is there anything else I can do before #3260 becomes generally available? Thanks. |
There is. Just define a custom implementation of class LocaleSafeSystemCategory : public std::error_category
{
const char* name() const noexcept override
{
return "locale-safe system error category";
}
std::string message(const int errorValue) const override
{
// The implementation of this would be like the GetSystemErrorMesssage
// function I shared above. It might be better move this to a .cpp file
// because it avoids having a Win32 dependency in anything that includes
// the header defining this class.
}
}; Then, wherever you would normally do something like this: throw std::system_error(GetLastError(),
std::system_category(),
"CreateFileA"); You would instead in theory do this: throw std::system_error(GetLastError(),
LocaleSafeSystemCategory{},
"CreateFileA"); I say in theory because, as is common in many codebases, I tend to never directly do a #define THROW_SYSTEM_ERROR(name, value, category) \
do \
{ \
throw std::system_error(value, \
category, \
#name "() failed in " __FUNCTION__ "()"); \
} while (false)
#if _MSC_VER >= 1930
# define THROW_WIN32_ERROR(name, win32ErrorValue) \
THROW_SYSTEM_ERROR(name, win32ErrorValue, LocaleSafeSystemCategory{})
#else
# define THROW_WIN32_ERROR(name, win32ErrorValue) \
THROW_SYSTEM_ERROR(name, win32ErrorValue, std::system_category())
#endif
#define THROW_LAST_WIN32_ERROR(name) \
THROW_WIN32_ERROR(name, GetLastError())
#define THROW_POSIX_ERROR(name, posixErrorValue) \
THROW_SYSTEM_ERROR(name, posixErrorValue, std::generic_category())
#define THROW_LAST_POSIX_ERROR(name) \
THROW_POSIX_ERROR(name, errno) |
That's a good idea. Thanks for clearly explaining it! Unfortunately, in my case, I don't already have an existing customization point like your macro, and I'd have a lot of code to audit. What I did discover, though, is that if you define implementations of all three of the |
I download visual studio 2022 17.4.1 and was surprised to see that "unknown error" is returned from
std::error_code
usingstd::system_categoey()
as the error category while it was reporting the correct error message before.After searching on the internet, I found someone complaining about the same issue here : "unknown error" from std::error_code on Windows
The solution was to set the system locale to en-US from "administrative language settings".
I tested it on two windows installations:
Windows 11 with locale set to "en-GB" and worked well when changed to "en-US"
Windows 10 with locale set to "ar-EG" and worked well when changed to "en-US"
I have also visual studio 2019 installed and the binary produced by it works as expected without needing to change the system locale
The issue seems to have appeared in
__std_system_error_allocate_message
insyserror_import_lib.cpp
after this commit <system_error>: explicitly pass the system locale ID for system_categ…To mention also: both windows installs have "English (United States)" as the "Windows display language" but had system locale different from "en-US"
EDIT: I set both display language and system locale to "ar-EG" but still "unknown error" is returned. Only when system locale is set to en-US the correct message is returned disregarding the display language
Also tracked by internal VSO-1735227 / AB#1735227 .
The text was updated successfully, but these errors were encountered: