TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

5.4 KiB

Windows 中文支持指南

问题

在 Windows 控制台显示中文时会出现乱码:

Win32 Hello World 绋嬪簭
鏄剧ず娑堟伅妗?..

原因

  1. Windows 控制台默认编码:GBK(代码页 936)
  2. 源文件编码:UTF-8
  3. 编码不匹配:导致乱码

解决方案

方案 1:程序内设置 UTF-8(推荐)

在 PHP 代码开始时设置控制台为 UTF-8:

function main()
{
    // Set console to UTF-8 for proper Chinese character display
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        exec('chcp 65001 > nul 2>&1');
    }
    
    echo "显示中文消息...\n";
}

优点:

  • 自动设置,用户无需手动操作
  • 跨平台兼容(只在 Windows 上执行)
  • 简单可靠

方案 2:手动设置控制台

运行程序前手动设置:

chcp 65001
.\win32_hello.exe

缺点:

  • 每次运行都需要手动设置
  • 用户体验不好

方案 3:C++ 层使用宽字符 API

对于 Windows API(如 MessageBox),使用宽字符版本:

Int php_messagebox(Int hWnd, String text, String caption, Int uType) {
    // Convert UTF-8 to UTF-16 for Windows API
    int wtext_len = MultiByteToWideChar(CP_UTF8, 0, text.data(), -1, NULL, 0);
    wchar_t* wtext = new wchar_t[wtext_len];
    MultiByteToWideChar(CP_UTF8, 0, text.data(), -1, wtext, wtext_len);
    
    int wcaption_len = MultiByteToWideChar(CP_UTF8, 0, caption.data(), -1, NULL, 0);
    wchar_t* wcaption = new wchar_t[wcaption_len];
    MultiByteToWideChar(CP_UTF8, 0, caption.data(), -1, wcaption, wcaption_len);
    
    int result = MessageBoxW((HWND)hWnd, wtext, wcaption, (UINT)uType);
    
    delete[] wtext;
    delete[] wcaption;
    return result;
}

关键点:

  • 使用 MultiByteToWideChar 将 UTF-8 转换为 UTF-16
  • 使用 MessageBoxW(宽字符版本)而不是 MessageBoxA
  • 记得释放内存(delete[]

完整示例

PHP 层(hello-win.php)

<?php

function main()
{
    // 设置控制台为 UTF-8
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        exec('chcp 65001 > nul 2>&1');
    }
    
    echo "========================================\n";
    echo "  Win32 Hello World 程序\n";
    echo "========================================\n\n";
    
    echo "显示消息框...\n";
    $result = messagebox(0, 
        "Hello from PHP Compiler!\n\n" .
        "这是一个使用 PHPX 编译器创建的 Windows 程序。\n\n" .
        "当前时间: " . date('Y-m-d H:i:s'), 
        "Hello World", 0);
    echo "消息框返回值: " . $result . "\n\n";
    
    echo "程序结束。按任意键退出...\n";
}

C++ 层(winapi.cc)

#include <phpx.h>
#include <windows.h>

using namespace php;

// Show message box (with UTF-8 support)
Int php_messagebox(Int hWnd, String text, String caption, Int uType) {
    // Convert UTF-8 to UTF-16 for Windows API
    int wtext_len = MultiByteToWideChar(CP_UTF8, 0, text.data(), -1, NULL, 0);
    wchar_t* wtext = new wchar_t[wtext_len];
    MultiByteToWideChar(CP_UTF8, 0, text.data(), -1, wtext, wtext_len);
    
    int wcaption_len = MultiByteToWideChar(CP_UTF8, 0, caption.data(), -1, NULL, 0);
    wchar_t* wcaption = new wchar_t[wcaption_len];
    MultiByteToWideChar(CP_UTF8, 0, caption.data(), -1, wcaption, wcaption_len);
    
    int result = MessageBoxW((HWND)hWnd, wtext, wcaption, (UINT)uType);
    
    delete[] wtext;
    delete[] wcaption;
    return result;
}

编译和测试

# 清理并重新编译
Remove-Item -Recurse -Force build
php bin\compiler.php examples\win32-hello\project.yml

# 运行(会自动设置 UTF-8)
.\build\win32_hello.exe

预期输出

========================================
  Win32 Hello World 程序
========================================

显示消息框...
[消息框正确显示中文]
消息框返回值: 1

提示:要创建完整窗口,需要实现窗口过程函数和消息循环。
这需要在 C++ 层实现 WNDCLASS 注册和消息泵。

程序结束。按任意键退出...

常见问题

Q1: 为什么消息框还需要特殊处理?

A: Windows API 的 MessageBoxA 使用 ANSI 编码(GBK),而 MessageBoxW 使用 Unicode(UTF-16)。我们的字符串是 UTF-8,所以需要转换。

Q2: 可以不转换直接用吗?

A: 不可以。直接使用会导致:

  • 控制台输出:乱码
  • 消息框:乱码或空白

Q3: 其他 Windows API 也需要转换吗?

A: 是的,所有接受字符串的 Windows API 都应该使用宽字符版本(带 W 后缀):

  • CreateWindowExW 而不是 CreateWindowExA
  • SetWindowTextW 而不是 SetWindowTextA
  • 等等...

Q4: Linux/macOS 需要这样处理吗?

A: 不需要。Linux/macOS 原生支持 UTF-8,可以直接使用。

最佳实践

  1. 始终在程序开始时设置 UTF-8
  2. Windows API 使用宽字符版本(W 后缀)
  3. UTF-8 ↔ UTF-16 转换后记得释放内存
  4. 源文件保存为 UTF-8 编码(无 BOM)
  5. 避免混用 ANSI 和 Unicode API

参考资源