在 C++ 中将中文宽字符串转换为常规字符串时出现奇怪的 unicode 错误

Strange unicode error when converting Chinese wide strings to regular strings in C++

我的一些中文软件用户注意到当我的 Windows C++ 代码试图列出所有 运行 进程时抛出一个奇怪的 C++ 异常:

在多字节的目标代码页中,没有此 Unicode 字符可以映射到的字符。

翻译成英文大致意思是:

There are no characters to which this Unicode character can be mapped in the multi-byte target code page.

打印这个的代码是:

try
{
    list_running_processes();
}
catch (std::runtime_error &exception)
{
    LOG_S(ERROR) << exception.what();
    return EXIT_FAILURE;
}

最有可能的罪魁祸首源代码是:

std::vector<running_process_t> list_running_processes()
{
    std::vector<running_process_t> running_processes;

    const auto snapshot_handle = unique_handle(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
    if (snapshot_handle.get() == INVALID_HANDLE_VALUE)
    {
        throw std::runtime_error("CreateToolhelp32Snapshot() failed");
    }
    
    PROCESSENTRY32 process_entry{};
    process_entry.dwSize = sizeof process_entry;

    if (Process32First(snapshot_handle.get(), &process_entry))
    {
        do
        {
            const auto process_id = process_entry.th32ProcessID;
            const auto executable_file_path = get_file_path(process_id);
            // *** HERE ***
            const auto process_name = wide_string_to_string(process_entry.szExeFile);
            running_processes.emplace_back(executable_file_path, process_name, process_id);
        } while (Process32Next(snapshot_handle.get(), &process_entry));
    }

    return running_processes;
}

或者:

std::string get_file_path(const DWORD process_id)
{
    std::string file_path;
    const auto snapshot_handle = unique_handle(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process_id));
    MODULEENTRY32W module_entry32{};
    module_entry32.dwSize = sizeof(MODULEENTRY32W);
    if (Module32FirstW(snapshot_handle.get(), &module_entry32))
    {
        do
        {
            if (module_entry32.th32ProcessID == process_id) 
            {
                return wide_string_to_string(module_entry32.szExePath); // *** HERE ***
            }
        } while (Module32NextW(snapshot_handle.get(), &module_entry32));
    }

    return file_path;
}

这是执行从 std::wstring 到常规 std::string 的转换的代码:

std::string wide_string_to_string(const std::wstring& wide_string)
{
    if (wide_string.empty())
    {
        return std::string();
    }

    const auto size_needed = WideCharToMultiByte(CP_UTF8, 0, &wide_string.at(0),
        static_cast<int>(wide_string.size()), nullptr, 0, nullptr, nullptr);
    std::string str_to(size_needed, 0);
    WideCharToMultiByte(CP_UTF8, 0, &wide_string.at(0), static_cast<int>(wide_string.size()), &str_to.at(0),
        size_needed, nullptr, nullptr);
    return str_to;
}

这在中文文件路径或中文 Windows 等上会失败吗?该代码在常规西方 Windows 机器上运行良好。如果我在这里遗漏了任何重要信息,请告诉我,因为我现在无法在无法访问其中一台受影响的机器的情况下自行调试或测试它。

我设法在中文机器上进行了测试,事实证明,如果文件路径包含例如,将文件路径从宽字符串转换为常规字符串,将产生错误的文件路径输出。中文(non-ASCII)符号。

我可以通过将对 wide_string_to_string() 的调用替换为 std::filesystem::path(wide_string_file_path).string() 来修复此错误,因为 std::filesystem API 将正确处理文件路径的转换,这与 wide_string_to_string().