From d50aa88504b59f11e2efff7d47e89ecfce857ec6 Mon Sep 17 00:00:00 2001 From: rango Date: Thu, 30 Apr 2026 10:31:53 +0800 Subject: [PATCH] =?UTF-8?q?docs(examples):=20=E6=B7=BB=E5=8A=A0=20Win32=20?= =?UTF-8?q?Hello=20World=20=E7=A4=BA=E4=BE=8B=E9=A1=B9=E7=9B=AE=E5=92=8C?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 Windows 中文支持指南文档 - 添加 PHPX 编译器 C++ 函数导出规范文档 - 添加编码指南文档 - 创建 hello-win.php 主程序文件 - 创建 main.php 简单消息框示例文件 - 创建 window.php 完整窗口示例文件 - 添加项目配置文件 project.yml - 创建 README.md 项目说明文档 - 添加 C++ Windows API 封装实现文件 winapi.cc - 添加 C++ 函数声明桩文件 winapi.stub.php --- examples/win32-hello/CHINESE_SUPPORT_GUIDE.md | 201 ++++++++++++ .../win32-hello/CPP_FUNCTION_EXPORT_GUIDE.md | 290 ++++++++++++++++++ examples/win32-hello/ENCODING_GUIDE.md | 112 +++++++ examples/win32-hello/README.md | 120 ++++++++ examples/win32-hello/cpp-src/winapi.cc | 65 ++++ examples/win32-hello/cpp-src/winapi.stub.php | 24 ++ examples/win32-hello/hello-win.php | 31 ++ examples/win32-hello/main.php | 61 ++++ examples/win32-hello/project.yml | 6 + examples/win32-hello/window.php | 86 ++++++ 10 files changed, 996 insertions(+) create mode 100644 examples/win32-hello/CHINESE_SUPPORT_GUIDE.md create mode 100644 examples/win32-hello/CPP_FUNCTION_EXPORT_GUIDE.md create mode 100644 examples/win32-hello/ENCODING_GUIDE.md create mode 100644 examples/win32-hello/README.md create mode 100644 examples/win32-hello/cpp-src/winapi.cc create mode 100644 examples/win32-hello/cpp-src/winapi.stub.php create mode 100644 examples/win32-hello/hello-win.php create mode 100644 examples/win32-hello/main.php create mode 100644 examples/win32-hello/project.yml create mode 100644 examples/win32-hello/window.php diff --git a/examples/win32-hello/CHINESE_SUPPORT_GUIDE.md b/examples/win32-hello/CHINESE_SUPPORT_GUIDE.md new file mode 100644 index 00000000..ec620c6f --- /dev/null +++ b/examples/win32-hello/CHINESE_SUPPORT_GUIDE.md @@ -0,0 +1,201 @@ +# Windows 中文支持指南 + +## 问题 + +在 Windows 控制台显示中文时会出现乱码: +``` +Win32 Hello World 绋嬪簭 +鏄剧ず娑堟伅妗?.. +``` + +## 原因 + +1. **Windows 控制台默认编码**:GBK(代码页 936) +2. **源文件编码**:UTF-8 +3. **编码不匹配**:导致乱码 + +## 解决方案 + +### ✅ 方案 1:程序内设置 UTF-8(推荐) + +在 PHP 代码开始时设置控制台为 UTF-8: + +```php +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:手动设置控制台 + +运行程序前手动设置: + +```powershell +chcp 65001 +.\win32_hello.exe +``` + +**缺点:** +- ❌ 每次运行都需要手动设置 +- ❌ 用户体验不好 + +### 方案 3:C++ 层使用宽字符 API + +对于 Windows API(如 MessageBox),使用宽字符版本: + +```cpp +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 + 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) + +```cpp +#include +#include + +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; +} +``` + +## 编译和测试 + +```powershell +# 清理并重新编译 +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** + +## 参考资源 + +- [Windows Unicode Documentation](https://docs.microsoft.com/en-us/windows/win32/intl/unicode-in-the-windows-api) +- [MultiByteToWideChar Function](https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar) +- [Code Pages](https://docs.microsoft.com/en-us/windows/win32/intl/code-pages) diff --git a/examples/win32-hello/CPP_FUNCTION_EXPORT_GUIDE.md b/examples/win32-hello/CPP_FUNCTION_EXPORT_GUIDE.md new file mode 100644 index 00000000..1ea1300d --- /dev/null +++ b/examples/win32-hello/CPP_FUNCTION_EXPORT_GUIDE.md @@ -0,0 +1,290 @@ +# PHPX 编译器 - C++ 函数导出规范 + +## 概述 + +在 PHPX 编译器中,C++ 函数可以被导出为 PHP 函数,供 PHP 代码调用。这需要遵循特定的规范和约定。 + +## 三大必要条件 + +### 1. 函数名必须以 `php_` 为前缀 + +```cpp +// ✅ 正确:以 php_ 为前缀 +Int php_messagebox(Int hWnd, String text, String caption, Int uType) { + // 实现代码 +} + +// ❌ 错误:缺少 php_ 前缀 +Int messagebox(Int hWnd, String text, String caption, Int uType) { + // 这不会被导出到 PHP +} +``` + +**命名规则:** +- C++ 函数名:`php_messagebox()` +- PHP 调用名:`messagebox()`(自动去掉 `php_` 前缀) + +### 2. 只能使用 PHPX 类型作为参数和返回值 + +**支持的 PHPX 类型:** + +| PHPX 类型 | PHP 对应类型 | 说明 | +|-----------|-------------|------| +| `Int` | `int` | 整数 | +| `Bool` | `bool` | 布尔值 | +| `Double` | `float` | 浮点数 | +| `String` | `string` | 字符串 | +| `Array` | `array` | 数组 | +| `Object` | `object` | 对象 | +| `Variant` | `mixed` | 混合类型 | +| `void` | 无返回值 | 仅用于返回值 | + +```cpp +// ✅ 正确:使用 PHPX 类型 +Int php_add(Int a, Int b) { + return a + b; +} + +String php_greet(String name) { + return "Hello, " + name + "!"; +} + +// ❌ 错误:使用原生 C/C++ 类型 +int php_add(int a, int b) { // 错误! + return a + b; +} + +char* php_greet(char* name) { // 错误! + return name; +} +``` + +### 3. 必须在 `.stub.php` 文件中声明 + +**Stub 文件的作用:** +- 只包含函数签名(参数和返回值类型) +- 不包含具体实现代码 +- 让编译器知道有哪些 C++ 函数可供 PHP 调用 + +**Stub 文件示例** (`winapi.stub.php`): + +```php + + +using namespace php; + +// 注意:函数名必须以 php_ 为前缀 +Int php_add(Int a, Int b) { + return a + b; +} + +String php_concat(String str1, String str2) { + return str1 + str2; +} + +Bool php_is_even(Int number) { + return (number % 2 == 0); +} +``` + +### 3. PHP 调用文件 (`main.php`) + +```php + nul'); + + echo "显示消息框...\n"; +} +``` + +### Option 3: Use Windows API for Unicode + +For message boxes and Windows GUI, use wide character functions: + +```cpp +// In C++ file +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; +} +``` + +## Best Practices + +1. **Use English for code comments and strings** - Most portable solution +2. **Save all files as UTF-8 without BOM** - Standard for modern development +3. **Avoid mixing encodings** - Keep consistency across all files +4. **Test on target systems** - Different Windows versions may have different defaults + +## Current Status + +All files in `examples/win32-hello/` now use English: +- ✅ hello-win.php +- ✅ main.php +- ✅ window.php +- ✅ cpp-src/winapi.cc +- ✅ cpp-src/winapi.stub.php + +Rebuild to see the changes: + +```powershell +php bin\compiler.php examples\win32-hello\project.yml +.\build\win32_hello.exe +``` + +Expected output: +``` +======================================== + Win32 Hello World Program +======================================== + +Showing message box... +Message box return value: 1 + +Note: To create a full window, you need to implement window procedure and message loop. +This requires WNDCLASS registration and message pump in C++ layer. + +Program ended. Press any key to exit... +``` diff --git a/examples/win32-hello/README.md b/examples/win32-hello/README.md new file mode 100644 index 00000000..d331dd30 --- /dev/null +++ b/examples/win32-hello/README.md @@ -0,0 +1,120 @@ +# Win32 Hello World 示例 + +这是一个使用 PHPX 编译器创建的最简单 Windows 图形界面程序示例。 + +## 项目结构 + +``` +win32-hello/ +├── hello-win.php # 主程序(使用 C++ 辅助函数) +├── window.php # 纯 PHP 版本(需要 Windows API 声明) +├── main.php # 最简单的消息框示例 +├── cpp-src/ +│ └── winapi.cc # C++ 实现的 Windows API 封装 +└── project.yml # 项目配置文件 +``` + +## 编译和运行 + +### 方法 1: 使用项目配置(推荐) + +```powershell +cd examples\win32-hello +php ..\..\bin\compiler.php project.yml +.\build\win32-hello.exe +``` + +### 方法 2: 直接编译单个文件 + +```powershell +# 编译最简单的版本 +php bin\compiler.php examples\win32-hello\main.php + +# 运行 +.\main.exe +``` + +## 代码说明 + +### C++ 函数导出规范 + +要让 C++ 函数能被 PHP 调用,必须满足以下条件: + +1. **函数名必须以 `php_` 为前缀** + - 例如:`php_messagebox()` 在 PHP 中调用时为 `messagebox()` + +2. **只能使用 PHPX 类型作为参数和返回值** + - `Int`, `Bool`, `String`, `Double`, `Array`, `Object`, `Variant` 等 + - 不能使用原生 C/C++ 类型(如 `int`, `char*` 等) + +3. **必须在 `.stub.php` 文件中声明** + - stub 文件只包含函数签名(参数和返回值) + - 不包含具体实现代码 + - 实现代码在对应的 `.cc` 或 `.cpp` 文件中 + +### 示例结构 + +**1. Stub 声明文件** (`cpp-src/winapi.stub.php`): +```php + +#include + +using namespace php; + +// 函数名必须以 php_ 为前缀 +Int php_messagebox(Int hWnd, String text, String caption, Int uType) { + return MessageBox((HWND)hWnd, text.c_str(), caption.c_str(), (UINT)uType); +} +``` + +**3. PHP 调用文件** (`hello-win.php`): +```php + +#include + +using namespace php; + +/** + * Windows API wrapper functions + * Note: Function names must be prefixed with php_ to be callable from 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; +} + +// Get module handle +Int php_get_module_handle(String moduleName) { + HMODULE hModule = GetModuleHandle(moduleName.length() == 0 ? NULL : moduleName.data()); + return (Int)hModule; +} + +// Create window (simplified version) +Int php_create_window(String className, String windowName, Int style, Int x, Int y, Int width, Int height) { + HWND hWnd = CreateWindowEx( + 0, // extended style + className.data(), // class name + windowName.data(), // window title + (DWORD)style, // window style + (int)x, (int)y, // position + (int)width, (int)height, // size + NULL, // parent window + NULL, // menu + GetModuleHandle(NULL), // instance handle + NULL // extra parameters + ); + return (Int)hWnd; +} + +// Show window +Bool php_show_window(Int hWnd, Int cmdShow) { + return ShowWindow((HWND)hWnd, cmdShow); +} + +// Update window +Bool php_update_window(Int hWnd) { + return UpdateWindow((HWND)hWnd); +} + +// Exit message loop +void php_post_quit_message(Int exitCode) { + PostQuitMessage((int)exitCode); +} diff --git a/examples/win32-hello/cpp-src/winapi.stub.php b/examples/win32-hello/cpp-src/winapi.stub.php new file mode 100644 index 00000000..88425428 --- /dev/null +++ b/examples/win32-hello/cpp-src/winapi.stub.php @@ -0,0 +1,24 @@ + nul 2>&1'); + } + + echo "========================================\n"; + echo " Win32 Hello World 程序\n"; + echo "========================================\n\n"; + + // Method 1: Use message box (simplest) + 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"; + + // Method 2: Create window (requires more code) + echo "提示:要创建完整窗口,需要实现窗口过程函数和消息循环。\n"; + echo "这需要在 C++ 层实现 WNDCLASS 注册和消息泵。\n\n"; + + echo "程序结束。按任意键退出...\n"; +} diff --git a/examples/win32-hello/main.php b/examples/win32-hello/main.php new file mode 100644 index 00000000..1417d0ed --- /dev/null +++ b/examples/win32-hello/main.php @@ -0,0 +1,61 @@ + nul 2>&1'); + } + + // Show a simple message box + $result = MessageBox(0, "Hello from PHP Compiler!\n\n这是一个使用 PHPX 编译器创建的 Windows 程序。", "Hello World", 0); + + echo "消息框返回值: " . $result . "\n"; + echo "程序结束。\n"; +} diff --git a/examples/win32-hello/project.yml b/examples/win32-hello/project.yml new file mode 100644 index 00000000..1babb902 --- /dev/null +++ b/examples/win32-hello/project.yml @@ -0,0 +1,6 @@ +name: win32-hello +version: 0.0.1 +mode: bin +sources: + - hello-win.php + - ./cpp-src diff --git a/examples/win32-hello/window.php b/examples/win32-hello/window.php new file mode 100644 index 00000000..009f91cf --- /dev/null +++ b/examples/win32-hello/window.php @@ -0,0 +1,86 @@ +