From 4292bb84993964ab3133f25894544f8756c944c1 Mon Sep 17 00:00:00 2001 From: wuyangfan <1102042793@qq.com> Date: Sun, 17 May 2026 22:35:18 +0800 Subject: [PATCH] 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. --- include/fmt/os.h | 2 ++ include/fmt/std.h | 12 ++++++++++++ src/os.cc | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/include/fmt/os.h b/include/fmt/os.h index 3fbd6905..6a405cd4 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -117,6 +117,8 @@ FMT_API const std::error_category& system_category() noexcept; namespace detail { FMT_API void format_windows_error(buffer& out, int error_code, const char* message) noexcept; +FMT_API bool append_system_error_message_as_utf8(buffer& buf, + string_view message) noexcept; } FMT_API std::system_error vwindows_error(int error_code, string_view fmt, diff --git a/include/fmt/std.h b/include/fmt/std.h index b6b98bb7..2e33f718 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -546,6 +546,13 @@ struct formatter& buf, + string_view message) noexcept; +} +#endif + template <> struct formatter { private: format_specs specs_; @@ -584,7 +591,12 @@ template <> struct formatter { ctx); auto buf = memory_buffer(); 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()); +#endif } else { buf.append(string_view(ec.category().name())); buf.push_back(':'); diff --git a/src/os.cc b/src/os.cc index 0e1d582a..4e2cbcfa 100644 --- a/src/os.cc +++ b/src/os.cc @@ -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); } +namespace detail { +bool append_system_error_message_as_utf8(buffer& buf, + string_view message) noexcept { + FMT_TRY { + if (message.empty()) return true; + const int size = static_cast(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(wide_size), L'\0'); + if (MultiByteToWideChar(CP_ACP, 0, message.data(), size, wide.data(), + wide_size) == 0) + return false; + auto utf8_message = to_utf8(); + 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 buffered_file::~buffered_file() noexcept {