Merge bitcoin/bitcoin#32380: Modernize use of UTF-8 in Windows code

53e4951a5b Switch to ANSI Windows API in `fsbridge::fopen()` function (Hennadii Stepanov)
dbe770d921 Switch to ANSI Windows API in `Win32ErrorString()` function (Hennadii Stepanov)
06d0be4e22 Remove no longer necessary `WinCmdLineArgs` class (Hennadii Stepanov)
f366408492 cmake: Set process code page to UTF-8 on Windows (Hennadii Stepanov)
dccbb17806 Set minimum supported Windows version to 1903 (May 2019 Update) (Hennadii Stepanov)

Pull request description:

  The main goal is to remove [deprecated](https://github.com/bitcoin/bitcoin/issues/32361) code (removed in C++26).

  This PR employs Microsoft's modern [approach](https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page) to handling UTF-8:
  > Until recently, Windows has emphasized "Unicode" -W variants over -A APIs. However, recent releases have used the ANSI code page and -A APIs as a means to introduce UTF-8 support to apps. If the ANSI code page is configured for UTF-8, then -A APIs typically operate in UTF-8. This model has the benefit of supporting existing code built with -A APIs without any code changes.

  TODO:
  - [x] Handle application manifests properly when building with MSVC.
  - [x] Bump the minimum supported Windows version to 1903 (May 2019 Update).
  - [x] Remove all remaining use cases of the deprecated `std:wstring_convert`.
      - The instance in `subprocess.h` will be addressed in a follow-up PR, as additional tests are likely needed.
      - The usage in `common/system.cpp` is handled in https://github.com/bitcoin/bitcoin/pull/32566.

  Resolves partially https://github.com/bitcoin/bitcoin/issues/32361.

ACKs for top commit:
  laanwj:
    re-ACK 53e4951a5b
  hodlinator:
    re-ACK 53e4951a5b
  davidgumberg:
    untested crACK https://github.com/bitcoin/bitcoin/commit/53e4951a5b5b9d166d278db4240513d09b447f58

Tree-SHA512: 0dbe9badca8b979ac2b4814fea6e4a7e53c423a1c96cb76ce894253137d3640a87631a5b22b9645e8f0c2a36a107122eb19ed8e92978c17384ffa8b9ab9993b5
This commit is contained in:
Hennadii Stepanov committed 2025-10-28 22:41:07 +00:00
commit 3bb30658e6
16 files changed
+27 -80

No files matched your search

+6 -1
View File
@@ -1,10 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity <assemblyIdentity
type="win32" type="win32"
name="org.bitcoincore.${target}" name="org.bitcoincore.${target}"
version="${CLIENT_VERSION_MAJOR}.${CLIENT_VERSION_MINOR}.${CLIENT_VERSION_BUILD}.0" version="${CLIENT_VERSION_MAJOR}.${CLIENT_VERSION_MINOR}.${CLIENT_VERSION_BUILD}.0"
/> />
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security> <security>
<requestedPrivileges> <requestedPrivileges>
+2 -2
View File
@@ -35,8 +35,8 @@ wallet versions of Bitcoin Core are generally supported.
Compatibility Compatibility
============== ==============
Bitcoin Core is supported and tested on operating systems using the Bitcoin Core is supported and tested on the following operating systems or newer:
Linux Kernel 3.17+, macOS 14+, and Windows 10+. Bitcoin Linux Kernel 3.17, macOS 14, and Windows 10 (version 1903). Bitcoin
Core should also work on most other Unix-like systems but is not as Core should also work on most other Unix-like systems but is not as
frequently tested on them. It is not recommended to use Bitcoin Core on frequently tested on them. It is not recommended to use Bitcoin Core on
unsupported systems. unsupported systems.
+1
View File
@@ -410,6 +410,7 @@ if(BUILD_UTIL_CHAINSTATE)
add_executable(bitcoin-chainstate add_executable(bitcoin-chainstate
bitcoin-chainstate.cpp bitcoin-chainstate.cpp
) )
add_windows_application_manifest(bitcoin-chainstate)
# TODO: The `SKIP_BUILD_RPATH` property setting can be deleted # TODO: The `SKIP_BUILD_RPATH` property setting can be deleted
# in the future after reordering Guix script commands to # in the future after reordering Guix script commands to
# perform binary checks after the installation step. # perform binary checks after the installation step.
+2
View File
@@ -56,6 +56,8 @@ add_executable(bench_bitcoin
verify_script.cpp verify_script.cpp
) )
add_windows_application_manifest(bench_bitcoin)
include(TargetDataSources) include(TargetDataSources)
target_raw_data_sources(bench_bitcoin NAMESPACE benchmark::data target_raw_data_sources(bench_bitcoin NAMESPACE benchmark::data
data/block413567.raw data/block413567.raw
-4
View File
@@ -1328,10 +1328,6 @@ static int CommandLineRPC(int argc, char *argv[])
MAIN_FUNCTION MAIN_FUNCTION
{ {
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment(); SetupEnvironment();
if (!SetupNetworking()) { if (!SetupNetworking()) {
tfm::format(std::cerr, "Error: Initializing networking failed\n"); tfm::format(std::cerr, "Error: Initializing networking failed\n");
-4
View File
@@ -94,10 +94,6 @@ static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[
MAIN_FUNCTION MAIN_FUNCTION
{ {
ArgsManager& args = gArgs; ArgsManager& args = gArgs;
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
int exit_status; int exit_status;
std::unique_ptr<interfaces::Init> init = interfaces::MakeWalletInit(argc, argv, exit_status); std::unique_ptr<interfaces::Init> init = interfaces::MakeWalletInit(argc, argv, exit_status);
+3
View File
@@ -6,6 +6,7 @@
#include <clientversion.h> #include <clientversion.h>
#include <common/args.h> #include <common/args.h>
#include <common/system.h>
#include <util/fs.h> #include <util/fs.h>
#include <util/exec.h> #include <util/exec.h>
#include <util/strencodings.h> #include <util/strencodings.h>
@@ -61,6 +62,8 @@ static void ExecCommand(const std::vector<const char*>& args, std::string_view a
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
SetupEnvironment();
try { try {
CommandLine cmd{ParseCommandLine(argc, argv)}; CommandLine cmd{ParseCommandLine(argc, argv)};
if (cmd.show_version) { if (cmd.show_version) {
-5
View File
@@ -259,11 +259,6 @@ static bool AppInit(NodeContext& node)
MAIN_FUNCTION MAIN_FUNCTION
{ {
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
NodeContext node; NodeContext node;
int exit_status; int exit_status;
std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status); std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
-29
View File
@@ -19,8 +19,6 @@
#include <util/string.h> #include <util/string.h>
#ifdef WIN32 #ifdef WIN32
#include <codecvt>
#include <shellapi.h>
#include <shlobj.h> #include <shlobj.h>
#endif #endif
@@ -879,30 +877,3 @@ void ArgsManager::LogArgs() const
} }
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options); logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
} }
namespace common {
#ifdef WIN32
WinCmdLineArgs::WinCmdLineArgs()
{
wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
argv = new char*[argc];
args.resize(argc);
for (int i = 0; i < argc; i++) {
args[i] = utf8_cvt.to_bytes(wargv[i]);
argv[i] = &*args[i].begin();
}
LocalFree(wargv);
}
WinCmdLineArgs::~WinCmdLineArgs()
{
delete[] argv;
}
std::pair<int, char**> WinCmdLineArgs::get()
{
return std::make_pair(argc, argv);
}
#endif
} // namespace common
-17
View File
@@ -480,21 +480,4 @@ std::string HelpMessageGroup(const std::string& message);
*/ */
std::string HelpMessageOpt(const std::string& option, const std::string& message); std::string HelpMessageOpt(const std::string& option, const std::string& message);
namespace common {
#ifdef WIN32
class WinCmdLineArgs
{
public:
WinCmdLineArgs();
~WinCmdLineArgs();
std::pair<int, char**> get();
private:
int argc;
char** argv;
std::vector<std::string> args;
};
#endif
} // namespace common
#endif // BITCOIN_COMMON_ARGS_H #endif // BITCOIN_COMMON_ARGS_H
+2
View File
@@ -12,6 +12,7 @@
#include <util/time.h> #include <util/time.h>
#ifdef WIN32 #ifdef WIN32
#include <cassert>
#include <codecvt> #include <codecvt>
#include <compat/compat.h> #include <compat/compat.h>
#include <windows.h> #include <windows.h>
@@ -83,6 +84,7 @@ void SetupEnvironment()
setenv("LC_ALL", "C.UTF-8", 1); setenv("LC_ALL", "C.UTF-8", 1);
} }
#elif defined(WIN32) #elif defined(WIN32)
assert(GetACP() == CP_UTF8);
// Set the default input/output charset is utf-8 // Set the default input/output charset is utf-8
SetConsoleCP(CP_UTF8); SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
-5
View File
@@ -478,11 +478,6 @@ static void SetupUIArgs(ArgsManager& argsman)
int GuiMain(int argc, char* argv[]) int GuiMain(int argc, char* argv[])
{ {
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv); std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
SetupEnvironment(); SetupEnvironment();
+2
View File
@@ -14,6 +14,8 @@ add_executable(test_bitcoin-qt
../../init/bitcoin-qt.cpp ../../init/bitcoin-qt.cpp
) )
add_windows_application_manifest(test_bitcoin-qt)
target_link_libraries(test_bitcoin-qt target_link_libraries(test_bitcoin-qt
core_interface core_interface
bitcoinqt bitcoinqt
+3
View File
@@ -135,6 +135,9 @@ add_executable(fuzz
vecdeque.cpp vecdeque.cpp
versionbits.cpp versionbits.cpp
) )
add_windows_application_manifest(fuzz)
target_link_libraries(fuzz target_link_libraries(fuzz
core_interface core_interface
fuzzer_interface fuzzer_interface
+1 -3
View File
@@ -12,7 +12,6 @@
#include <sys/utsname.h> #include <sys/utsname.h>
#include <unistd.h> #include <unistd.h>
#else #else
#include <codecvt>
#include <limits> #include <limits>
#include <windows.h> #include <windows.h>
#endif #endif
@@ -28,8 +27,7 @@ FILE *fopen(const fs::path& p, const char *mode)
#ifndef WIN32 #ifndef WIN32
return ::fopen(p.c_str(), mode); return ::fopen(p.c_str(), mode);
#else #else
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> utf8_cvt; return ::fopen(p.utf8string().c_str(), mode);
return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str());
#endif #endif
} }
+5 -10
View File
@@ -12,8 +12,6 @@
#if defined(WIN32) #if defined(WIN32)
#include <windows.h> #include <windows.h>
#include <locale>
#include <codecvt>
#endif #endif
std::string SysErrorString(int err) std::string SysErrorString(int err)
@@ -41,16 +39,13 @@ std::string SysErrorString(int err)
#if defined(WIN32) #if defined(WIN32)
std::string Win32ErrorString(int err) std::string Win32ErrorString(int err)
{ {
wchar_t buf[256]; char buf[256];
buf[0] = 0; buf[0] = 0;
if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, ARRAYSIZE(buf), nullptr)) buf, ARRAYSIZE(buf), nullptr)) {
{ return strprintf("%s (%d)", buf, err);
return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err); } else {
}
else
{
return strprintf("Unknown error (%d)", err); return strprintf("Unknown error (%d)", err);
} }
} }