fix: convert std::error_code messages to UTF-8 on Windows

When formatting with {:s}, system error messages may use the active
code page instead of UTF-8. Convert from ACP via UTF-16 before writing.

Fixes #4436.
This commit is contained in:
wuyangfan 2026-05-17 22:35:18 +08:00
parent 2f18a88e68
commit 4292bb8499
3 changed files with 37 additions and 0 deletions

View File

@ -117,6 +117,8 @@ FMT_API const std::error_category& system_category() noexcept;
namespace detail { namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept; const char* message) noexcept;
FMT_API bool append_system_error_message_as_utf8(buffer<char>& buf,
string_view message) noexcept;
} }
FMT_API std::system_error vwindows_error(int error_code, string_view fmt, FMT_API std::system_error vwindows_error(int error_code, string_view fmt,

View File

@ -546,6 +546,13 @@ struct formatter<Variant, Char,
#endif // FMT_CPP_LIB_VARIANT #endif // FMT_CPP_LIB_VARIANT
#ifdef _WIN32
namespace detail {
FMT_API bool append_system_error_message_as_utf8(buffer<char>& buf,
string_view message) noexcept;
}
#endif
template <> struct formatter<std::error_code> { template <> struct formatter<std::error_code> {
private: private:
format_specs specs_; format_specs specs_;
@ -584,7 +591,12 @@ template <> struct formatter<std::error_code> {
ctx); ctx);
auto buf = memory_buffer(); auto buf = memory_buffer();
if (specs_.type() == presentation_type::string) { if (specs_.type() == presentation_type::string) {
#ifdef _WIN32
if (!detail::append_system_error_message_as_utf8(buf, ec.message()))
buf.append(ec.message());
#else
buf.append(ec.message()); buf.append(ec.message());
#endif
} else { } else {
buf.append(string_view(ec.category().name())); buf.append(string_view(ec.category().name()));
buf.push_back(':'); buf.push_back(':');

View File

@ -165,6 +165,29 @@ void report_windows_error(int error_code, const char* message) noexcept {
do_report_error(detail::format_windows_error, error_code, message); do_report_error(detail::format_windows_error, error_code, message);
} }
namespace detail {
bool append_system_error_message_as_utf8(buffer<char>& buf,
string_view message) noexcept {
FMT_TRY {
if (message.empty()) return true;
const int size = static_cast<int>(message.size());
const int wide_size =
MultiByteToWideChar(CP_ACP, 0, message.data(), size, nullptr, 0);
if (wide_size == 0) return false;
std::wstring wide(static_cast<size_t>(wide_size), L'\0');
if (MultiByteToWideChar(CP_ACP, 0, message.data(), size, wide.data(),
wide_size) == 0)
return false;
auto utf8_message = to_utf8<wchar_t>();
if (!utf8_message.convert(wide)) return false;
buf.append(string_view(utf8_message.c_str(), utf8_message.size()));
return true;
}
FMT_CATCH(...) {}
return false;
}
} // namespace detail
#endif // _WIN32 #endif // _WIN32
buffered_file::~buffered_file() noexcept { buffered_file::~buffered_file() noexcept {