From e56d32ffe8573a33bb6adc4bef46a3a45df9fd21 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 00:48:31 -0800 Subject: [PATCH 1/9] Extract `FormatMessageA` flags for clarity. --- stl/src/syserror_import_lib.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index e2f2d2b7c30..2b1eed83d82 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -46,9 +46,9 @@ extern "C" { if (_Ret == 0) { _Lang_id = 0; } + constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; const unsigned long _Chars = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); + FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); return _CSTD __std_get_string_size_without_trailing_whitespace(*_Ptr_str, _Chars); } From 32a5e35e0c6da7dc4e1f4768d0603d3421c6c6c2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 01:06:10 -0800 Subject: [PATCH 2/9] Hardcode 0x0409, the language ID for "en-US". --- stl/src/syserror_import_lib.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index 2b1eed83d82..edd3190c36e 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -40,13 +40,8 @@ extern "C" { // convert to name of Windows error, return 0 for failure, otherwise return number of chars in buffer // __std_system_error_deallocate_message should be called even if 0 is returned // pre: *_Ptr_str == nullptr - DWORD _Lang_id; - const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, - reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); - if (_Ret == 0) { - _Lang_id = 0; - } constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + constexpr auto _Lang_id = 0x0409; // 1033 decimal, "en-US" locale const unsigned long _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); From 2c72feda2db9771dc8246ff9d29d278b977ceb79 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 01:48:19 -0800 Subject: [PATCH 3/9] Add detailed explanation. --- stl/src/syserror_import_lib.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index edd3190c36e..c80bf93f75c 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -40,8 +40,22 @@ extern "C" { // convert to name of Windows error, return 0 for failure, otherwise return number of chars in buffer // __std_system_error_deallocate_message should be called even if 0 is returned // pre: *_Ptr_str == nullptr + + // We always request US English for system_category() messages. + // This is consistent with generic_category(), which uses a table of US English strings in the STL. + // See GH-2451 and GH-3254 for the history here - we previously tried to localize system_category() messages, + // but attempting to use FormatMessageA's behavior for language ID 0 and attempting to use the system locale + // had various failure scenarios. + // Using US English (which is FormatMessageA's final fallback for language ID 0) + // is likely to succeed with whatever the end-user's system configuration is. + // In general, system_error messages aren't directly useful to end-users - they're meant for programmer-users. + // Of course, the programmer-user might not speak US English, but machine translation of the message + // (and the numeric value of the error code) should help them understand the error. + // The previous failure scenarios of "unknown error" or a string of question marks were completely unhelpful. + constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; constexpr auto _Lang_id = 0x0409; // 1033 decimal, "en-US" locale + const unsigned long _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); From 6f3d866acaa9bbbbb96a959e969007a3393d5a89 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 01:49:05 -0800 Subject: [PATCH 4/9] Cleanup: `FormatMessageA` returns `DWORD`, which is `unsigned long`, but `auto` is simpler. --- stl/src/syserror_import_lib.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index c80bf93f75c..f5168c719a8 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -56,7 +56,7 @@ extern "C" { constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; constexpr auto _Lang_id = 0x0409; // 1033 decimal, "en-US" locale - const unsigned long _Chars = + const auto _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); return _CSTD __std_get_string_size_without_trailing_whitespace(*_Ptr_str, _Chars); From 7c1e1d9f381e29c89bbaab14c1fd3215bd61252a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 15:21:02 -0800 Subject: [PATCH 5/9] Remove detailed explanation (will be reworked). --- stl/src/syserror_import_lib.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index f5168c719a8..64f11e8eb7d 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -41,18 +41,6 @@ extern "C" { // __std_system_error_deallocate_message should be called even if 0 is returned // pre: *_Ptr_str == nullptr - // We always request US English for system_category() messages. - // This is consistent with generic_category(), which uses a table of US English strings in the STL. - // See GH-2451 and GH-3254 for the history here - we previously tried to localize system_category() messages, - // but attempting to use FormatMessageA's behavior for language ID 0 and attempting to use the system locale - // had various failure scenarios. - // Using US English (which is FormatMessageA's final fallback for language ID 0) - // is likely to succeed with whatever the end-user's system configuration is. - // In general, system_error messages aren't directly useful to end-users - they're meant for programmer-users. - // Of course, the programmer-user might not speak US English, but machine translation of the message - // (and the numeric value of the error code) should help them understand the error. - // The previous failure scenarios of "unknown error" or a string of question marks were completely unhelpful. - constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; constexpr auto _Lang_id = 0x0409; // 1033 decimal, "en-US" locale From e64cd5683fd7cfa05c54ae52f9f7ec0ad0a45861 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 15:23:16 -0800 Subject: [PATCH 6/9] Restore `DWORD _Lang_id`, make `_Chars` modifiable too. --- stl/src/syserror_import_lib.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index 64f11e8eb7d..663d567a3e6 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -42,10 +42,10 @@ extern "C" { // pre: *_Ptr_str == nullptr constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - constexpr auto _Lang_id = 0x0409; // 1033 decimal, "en-US" locale - const auto _Chars = - FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); + DWORD _Lang_id = 0x0409; // 1033 decimal, "en-US" locale + + auto _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); return _CSTD __std_get_string_size_without_trailing_whitespace(*_Ptr_str, _Chars); } From d7fd4608e5bfcbc74cc456b79da856f98d9a711c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 15:54:34 -0800 Subject: [PATCH 7/9] If US English fails, fall back to the existing system locale logic. Co-authored-by: Jcr-dev --- stl/src/syserror_import_lib.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index 663d567a3e6..7fc7bbe8db5 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -47,6 +47,19 @@ extern "C" { auto _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); + if (_Chars == 0) { + LocalFree(*_Ptr_str); + *_Ptr_str = nullptr; + + const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, + reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); + if (_Ret == 0) { + _Lang_id = 0; + } + + _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); + } + return _CSTD __std_get_string_size_without_trailing_whitespace(*_Ptr_str, _Chars); } From 06384b76d2da545ffbd31a541dc9602e6d734be1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 20 Nov 2024 15:55:30 -0800 Subject: [PATCH 8/9] Comment the new technique. --- stl/src/syserror_import_lib.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index 7fc7bbe8db5..515b7dfb456 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -41,6 +41,12 @@ extern "C" { // __std_system_error_deallocate_message should be called even if 0 is returned // pre: *_Ptr_str == nullptr + // We start by requesting US English for system_category() messages. (See GH-2451 and GH-3254 for the history.) + // This is consistent with generic_category(), which uses a table of US English strings in the STL. + // In general, system_error messages aren't directly useful to end-users - they're meant for programmer-users. + // Of course, the programmer-user might not speak US English, but machine translation of the message + // (and the numeric value of the error code) should help them understand the error. + constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; DWORD _Lang_id = 0x0409; // 1033 decimal, "en-US" locale @@ -48,11 +54,15 @@ extern "C" { auto _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); if (_Chars == 0) { + // If FormatMessageA() failed for any reason, cleanup and restore this function's precondition. LocalFree(*_Ptr_str); *_Ptr_str = nullptr; + // Attempt to get the system locale's language ID. const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); + + // If we can't get the system locale, the final fallback is FormatMessageA()'s behavior for language ID 0. if (_Ret == 0) { _Lang_id = 0; } From 68432724261f2a37337f1c8d2bd47140cb02d1c2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 21 Nov 2024 12:24:25 -0800 Subject: [PATCH 9/9] Make 3 attempts: US English, system locale, ID 0. --- stl/src/syserror_import_lib.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index 515b7dfb456..b111662b4a3 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -49,21 +49,19 @@ extern "C" { constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - DWORD _Lang_id = 0x0409; // 1033 decimal, "en-US" locale - - auto _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); - - if (_Chars == 0) { - // If FormatMessageA() failed for any reason, cleanup and restore this function's precondition. - LocalFree(*_Ptr_str); - *_Ptr_str = nullptr; - - // Attempt to get the system locale's language ID. - const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, - reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); - - // If we can't get the system locale, the final fallback is FormatMessageA()'s behavior for language ID 0. - if (_Ret == 0) { + DWORD _Lang_id = 0; + DWORD _Chars = 0; + + for (int _Attempt = 0; _Attempt < 3 && _Chars == 0; ++_Attempt) { + if (_Attempt == 0) { + _Lang_id = 0x0409; // 1033 decimal, "en-US" locale + } else if (_Attempt == 1) { + const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, + reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); + if (_Ret == 0) { + continue; // If we can't get the system locale's language ID, skip this attempt + } + } else { _Lang_id = 0; }