From a8dfe793abd5d7505a21a3749686d1119d4ced9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 4 Jan 2026 22:54:12 +0100 Subject: [PATCH 1/4] util: add Utf8ToWide helper Add a Windows UTF-8-to-wide conversion helper using `MultiByteToWideChar(CP_UTF8, ...)`, so call sites can avoid deprecated `` / `std::wstring_convert` conversions. --- src/util/string.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/util/string.h | 4 ++++ 2 files changed, 42 insertions(+) diff --git a/src/util/string.cpp b/src/util/string.cpp index 507d9d317182..56cd78871256 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -4,6 +4,18 @@ #include +#ifdef WIN32 +#include + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +#include +#include +#endif + #include #include @@ -13,4 +25,30 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin if (search.empty()) return; in_out = std::regex_replace(in_out, std::regex(search), substitute); } + +#ifdef WIN32 +std::wstring Utf8ToWide(std::string_view utf8) +{ + if (utf8.empty()) return {}; + if (utf8.size() > static_cast(std::numeric_limits::max())) { + throw std::runtime_error("UTF-8 string is too long to convert"); + } + + const int src_size{static_cast(utf8.size())}; + const int dst_size{MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.data(), src_size, nullptr, 0)}; + if (dst_size == 0) { + const auto err{GetLastError()}; + throw std::runtime_error("MultiByteToWideChar failed: " + Win32ErrorString(err)); + } + + std::wstring wide(dst_size, 0); + const int result{MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.data(), src_size, wide.data(), dst_size)}; + if (result != dst_size) { + const auto err{GetLastError()}; + throw std::runtime_error("MultiByteToWideChar failed: " + Win32ErrorString(err)); + } + + return wide; +} +#endif } // namespace util diff --git a/src/util/string.h b/src/util/string.h index 330c2a2a61e0..d12e4c616472 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -260,6 +260,10 @@ template return obj.size() >= PREFIX_LEN && std::equal(std::begin(prefix), std::end(prefix), std::begin(obj)); } + +#ifdef WIN32 +std::wstring Utf8ToWide(std::string_view utf8); +#endif } // namespace util #endif // BITCOIN_UTIL_STRING_H From f75afbc20ced747a34e68f1cc0f94ebcd3dff3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 4 Jan 2026 22:55:54 +0100 Subject: [PATCH 2/4] util: avoid deprecated conversions on Windows Clang warns that `std::wstring_convert` and `std::codecvt_utf8_utf16` are deprecated. Use the shared `util::Utf8ToWide` helper when preparing wide strings for Windows APIs in `runCommand`, `ExecVp`, and `subprocess`. --- src/common/system.cpp | 4 ++-- src/util/exec.cpp | 7 ++++--- src/util/subprocess.h | 8 ++------ 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/common/system.cpp b/src/common/system.cpp index 72e9de100be6..2c302378c151 100644 --- a/src/common/system.cpp +++ b/src/common/system.cpp @@ -13,7 +13,6 @@ #ifdef WIN32 #include -#include #include #include #else @@ -56,7 +55,8 @@ void runCommand(const std::string& strCommand) #ifndef WIN32 int nErr = ::system(strCommand.c_str()); #else - int nErr = ::_wsystem(std::wstring_convert,wchar_t>().from_bytes(strCommand).c_str()); + const std::wstring wide_command{util::Utf8ToWide(strCommand)}; + int nErr = ::_wsystem(wide_command.c_str()); #endif if (nErr) { LogWarning("runCommand error: system(%s) returned %d", strCommand, nErr); diff --git a/src/util/exec.cpp b/src/util/exec.cpp index 6b1406134268..af293d828096 100644 --- a/src/util/exec.cpp +++ b/src/util/exec.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -24,16 +25,16 @@ int ExecVp(const char* file, char* const argv[]) return execvp(file, argv); #else std::vector escaped_args; - std::wstring_convert> converter; for (char* const* arg_ptr{argv}; *arg_ptr; ++arg_ptr) { - subprocess::util::quote_argument(converter.from_bytes(*arg_ptr), escaped_args.emplace_back(), false); + subprocess::util::quote_argument(util::Utf8ToWide(*arg_ptr), escaped_args.emplace_back(), false); } std::vector new_argv; new_argv.reserve(escaped_args.size() + 1); for (const auto& s : escaped_args) new_argv.push_back(s.c_str()); new_argv.push_back(nullptr); - return _wexecvp(converter.from_bytes(file).c_str(), new_argv.data()); + const std::wstring wide_file{util::Utf8ToWide(file)}; + return _wexecvp(wide_file.c_str(), new_argv.data()); #endif } diff --git a/src/util/subprocess.h b/src/util/subprocess.h index e90c165205ee..8233449708c1 100644 --- a/src/util/subprocess.h +++ b/src/util/subprocess.h @@ -37,6 +37,7 @@ Documentation for C++ subprocessing library. #define BITCOIN_UTIL_SUBPROCESS_H #include +#include #include #include @@ -59,10 +60,6 @@ Documentation for C++ subprocessing library. #define __USING_WINDOWS__ #endif -#ifdef __USING_WINDOWS__ - #include -#endif - extern "C" { #ifdef __USING_WINDOWS__ #include @@ -1102,7 +1099,6 @@ inline void Popen::execute_process() noexcept(false) } this->exe_name_ = vargs_[0]; - std::wstring_convert> converter; std::wstring argument; std::wstring command_line; bool first_arg = true; @@ -1113,7 +1109,7 @@ inline void Popen::execute_process() noexcept(false) } else { first_arg = false; } - argument = converter.from_bytes(arg); + argument = ::util::Utf8ToWide(arg); util::quote_argument(argument, command_line, false); } From df9fe139a8968788d71fb482bd6efa2096cdde76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 4 Jan 2026 22:57:04 +0100 Subject: [PATCH 3/4] subprocess: avoid unused fields on Windows When building for Windows, the fork/exec-specific `detail::Child` helper and `Popen::child_pid_` are compiled out, leaving their members unused and triggering `-Wunused-private-field` warnings. Guard these POSIX-only declarations with `#ifndef __USING_WINDOWS__`. --- src/util/subprocess.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/subprocess.h b/src/util/subprocess.h index 8233449708c1..4ec0a09265f6 100644 --- a/src/util/subprocess.h +++ b/src/util/subprocess.h @@ -740,6 +740,7 @@ struct ArgumentDeducer * This takes care of all the fork-exec logic * in the execute_child API. */ +#ifndef __USING_WINDOWS__ class Child { public: @@ -756,6 +757,7 @@ class Child Popen* parent_ = nullptr; int err_wr_pipe_ = -1; }; +#endif // !__USING_WINDOWS__ // Fwd Decl. class Streams; @@ -929,7 +931,9 @@ class Popen { public: friend struct detail::ArgumentDeducer; +#ifndef __USING_WINDOWS__ friend class detail::Child; +#endif template Popen(const std::string& cmd_args, Args&& ...args): @@ -1032,7 +1036,9 @@ class Popen std::vector cargv_; // Pid of the child process +#ifndef __USING_WINDOWS__ int child_pid_ = -1; +#endif int retcode_ = -1; }; From 3604201d1bb1691e16f551f41a6e324b9cab5b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 4 Jan 2026 23:03:14 +0100 Subject: [PATCH 4/4] leveldb: drop unused ScopedHandle move assignment Clang `-Wunused-member-function` warns about the unused `ScopedHandle::operator=(ScopedHandle&&)` helper in `env_windows.cc`. Remove it since this is an internal type only moved via construction, and keeping the dead operator provides no benefit. --- src/leveldb/util/env_windows.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/leveldb/util/env_windows.cc b/src/leveldb/util/env_windows.cc index 8897e2aa26b9..e33f83c813b5 100644 --- a/src/leveldb/util/env_windows.cc +++ b/src/leveldb/util/env_windows.cc @@ -81,11 +81,6 @@ class ScopedHandle { ScopedHandle& operator=(const ScopedHandle&) = delete; - ScopedHandle& operator=(ScopedHandle&& rhs) noexcept { - if (this != &rhs) handle_ = rhs.Release(); - return *this; - } - bool Close() { if (!is_valid()) { return true;