- 添加 Windows 中文支持指南文档 - 添加 PHPX 编译器 C++ 函数导出规范文档 - 添加编码指南文档 - 创建 hello-win.php 主程序文件 - 创建 main.php 简单消息框示例文件 - 创建 window.php 完整窗口示例文件 - 添加项目配置文件 project.yml - 创建 README.md 项目说明文档 - 添加 C++ Windows API 封装实现文件 winapi.cc - 添加 C++ 函数声明桩文件 winapi.stub.phppull/1/head
parent
2481eebd0c
commit
d50aa88504
10 changed files with 996 additions and 0 deletions
@ -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 |
||||||
|
<?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) |
||||||
|
|
||||||
|
```cpp |
||||||
|
#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; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 编译和测试 |
||||||
|
|
||||||
|
```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) |
||||||
@ -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 |
||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Windows API 封装函数的声明文件(stub) |
||||||
|
* 这些函数在 C++ 中实现,PHP 层只负责声明 |
||||||
|
*/ |
||||||
|
|
||||||
|
// 显示消息框 |
||||||
|
function messagebox(int $hWnd, string $text, string $caption, int $uType): int {} |
||||||
|
|
||||||
|
// 获取模块句柄 |
||||||
|
function get_module_handle(string $moduleName): int {} |
||||||
|
|
||||||
|
// 创建窗口 |
||||||
|
function create_window( |
||||||
|
string $className, |
||||||
|
string $windowName, |
||||||
|
int $style, |
||||||
|
int $x, |
||||||
|
int $y, |
||||||
|
int $width, |
||||||
|
int $height |
||||||
|
): int {} |
||||||
|
|
||||||
|
// 显示窗口 |
||||||
|
function show_window(int $hWnd, int $cmdShow): bool {} |
||||||
|
|
||||||
|
// 退出消息循环 |
||||||
|
function post_quit_message(int $exitCode): void {} |
||||||
|
``` |
||||||
|
|
||||||
|
**注意事项:** |
||||||
|
- Stub 文件必须是 `.stub.php` 扩展名 |
||||||
|
- 函数体为空(`{}`),不包含任何代码 |
||||||
|
- 参数类型和返回值类型必须与 C++ 实现一致 |
||||||
|
- 函数名不需要 `php_` 前缀(编译器会自动添加) |
||||||
|
|
||||||
|
## 完整示例 |
||||||
|
|
||||||
|
### 项目结构 |
||||||
|
|
||||||
|
``` |
||||||
|
my-extension/ |
||||||
|
├── main.php # PHP 主程序 |
||||||
|
├── cpp-src/ |
||||||
|
│ ├── mylib.stub.php # Stub 声明文件 |
||||||
|
│ └── mylib.cc # C++ 实现文件 |
||||||
|
└── project.yml # 项目配置 |
||||||
|
``` |
||||||
|
|
||||||
|
### 1. Stub 声明文件 (`cpp-src/mylib.stub.php`) |
||||||
|
|
||||||
|
```php |
||||||
|
<?php |
||||||
|
|
||||||
|
// 计算两个整数的和 |
||||||
|
function add(int $a, int $b): int {} |
||||||
|
|
||||||
|
// 拼接字符串 |
||||||
|
function concat(string $str1, string $str2): string {} |
||||||
|
|
||||||
|
// 判断是否为偶数 |
||||||
|
function is_even(int $number): bool {} |
||||||
|
``` |
||||||
|
|
||||||
|
### 2. C++ 实现文件 (`cpp-src/mylib.cc`) |
||||||
|
|
||||||
|
```cpp |
||||||
|
#include <phpx.h> |
||||||
|
|
||||||
|
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 |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() { |
||||||
|
// 直接调用 C++ 函数,无需额外声明 |
||||||
|
$sum = add(10, 20); |
||||||
|
echo "10 + 20 = $sum\n"; // 输出: 10 + 20 = 30 |
||||||
|
|
||||||
|
$greeting = concat("Hello, ", "World!"); |
||||||
|
echo "$greeting\n"; // 输出: Hello, World! |
||||||
|
|
||||||
|
if (is_even(42)) { |
||||||
|
echo "42 是偶数\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### 4. 项目配置 (`project.yml`) |
||||||
|
|
||||||
|
```yaml |
||||||
|
name: my-extension |
||||||
|
version: 0.0.1 |
||||||
|
sources: |
||||||
|
- main.php |
||||||
|
- ./cpp-src |
||||||
|
``` |
||||||
|
|
||||||
|
## 编译流程 |
||||||
|
|
||||||
|
``` |
||||||
|
1. 编译器读取 .stub.php 文件 |
||||||
|
↓ |
||||||
|
2. 生成对应的函数声明头文件 |
||||||
|
↓ |
||||||
|
3. 编译 C++ 实现文件(.cc/.cpp) |
||||||
|
↓ |
||||||
|
4. 链接所有目标文件 |
||||||
|
↓ |
||||||
|
5. 生成可执行文件或扩展 |
||||||
|
``` |
||||||
|
|
||||||
|
## 常见错误 |
||||||
|
|
||||||
|
### 错误 1:函数名没有 `php_` 前缀 |
||||||
|
|
||||||
|
```cpp |
||||||
|
// ❌ 错误 |
||||||
|
Int add(Int a, Int b) { |
||||||
|
return a + b; |
||||||
|
} |
||||||
|
|
||||||
|
// ✅ 正确 |
||||||
|
Int php_add(Int a, Int b) { |
||||||
|
return a + b; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**症状:** PHP 调用时提示函数未定义 |
||||||
|
|
||||||
|
### 错误 2:使用了原生 C/C++ 类型 |
||||||
|
|
||||||
|
```cpp |
||||||
|
// ❌ 错误 |
||||||
|
int php_add(int a, int b) { |
||||||
|
return a + b; |
||||||
|
} |
||||||
|
|
||||||
|
// ✅ 正确 |
||||||
|
Int php_add(Int a, Int b) { |
||||||
|
return a + b; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**症状:** 编译错误或类型不匹配 |
||||||
|
|
||||||
|
### 错误 3:没有在 stub 文件中声明 |
||||||
|
|
||||||
|
```cpp |
||||||
|
// C++ 中有实现 |
||||||
|
Int php_my_function(Int x) { |
||||||
|
return x * 2; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
但没有在 `.stub.php` 中声明。 |
||||||
|
|
||||||
|
**症状:** PHP 调用时提示函数未定义 |
||||||
|
|
||||||
|
### 错误 4:Stub 和 C++ 实现类型不一致 |
||||||
|
|
||||||
|
```php |
||||||
|
// stub.php |
||||||
|
function add(int $a, int $b): string {} // 返回 string |
||||||
|
``` |
||||||
|
|
||||||
|
```cpp |
||||||
|
// mylib.cc |
||||||
|
Int php_add(Int a, Int b) { // 返回 Int,不一致! |
||||||
|
return a + b; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**症状:** 编译错误或运行时类型错误 |
||||||
|
|
||||||
|
## 最佳实践 |
||||||
|
|
||||||
|
1. **组织文件结构** |
||||||
|
- 将相关的 stub 和 C++ 文件放在同一目录 |
||||||
|
- 使用有意义的文件名(如 `winapi.stub.php` 和 `winapi.cc`) |
||||||
|
|
||||||
|
2. **类型安全** |
||||||
|
- 始终使用正确的 PHPX 类型 |
||||||
|
- 避免类型转换,除非必要 |
||||||
|
|
||||||
|
3. **注释清晰** |
||||||
|
- 在 stub 文件中添加函数说明 |
||||||
|
- 在 C++ 实现中添加详细注释 |
||||||
|
|
||||||
|
4. **错误处理** |
||||||
|
- 在 C++ 函数中进行参数验证 |
||||||
|
- 使用 `zend_throw_error()` 抛出异常 |
||||||
|
|
||||||
|
5. **命名规范** |
||||||
|
- 使用小写字母和下划线分隔单词 |
||||||
|
- 保持函数名简洁明了 |
||||||
|
|
||||||
|
## 参考资源 |
||||||
|
|
||||||
|
- [PHPX 官方文档](https://github.com/swoole/phpx) |
||||||
|
- [examples/prime](../prime) - 完整的 C++ 混合编程示例 |
||||||
|
- [examples/win32-hello](../win32-hello) - Windows API 封装示例 |
||||||
@ -0,0 +1,112 @@ |
|||||||
|
# Encoding Guide for PHPX Compiler |
||||||
|
|
||||||
|
## Problem |
||||||
|
|
||||||
|
When using Chinese characters in source code, you may encounter garbled text (乱码) like: |
||||||
|
``` |
||||||
|
Win32 Hello World 绋嬪簭 |
||||||
|
鏄剧ず娑堟伅妗?.. |
||||||
|
``` |
||||||
|
|
||||||
|
This happens because: |
||||||
|
1. Windows console uses code page 936 (GBK) by default |
||||||
|
2. Source files are saved as UTF-8 |
||||||
|
3. The mismatch causes encoding issues |
||||||
|
|
||||||
|
## Solution |
||||||
|
|
||||||
|
### Option 1: Use English (Recommended) ✅ |
||||||
|
|
||||||
|
All example files now use English to avoid encoding issues: |
||||||
|
|
||||||
|
**Before:** |
||||||
|
```php |
||||||
|
echo "显示消息框...\n"; |
||||||
|
``` |
||||||
|
|
||||||
|
**After:** |
||||||
|
```php |
||||||
|
echo "Showing message box...\n"; |
||||||
|
``` |
||||||
|
|
||||||
|
### Option 2: Set Console to UTF-8 |
||||||
|
|
||||||
|
If you must use Chinese, set the console code page to UTF-8 before running: |
||||||
|
|
||||||
|
```powershell |
||||||
|
chcp 65001 |
||||||
|
.\win32_hello.exe |
||||||
|
``` |
||||||
|
|
||||||
|
Or in your PHP code, set it programmatically: |
||||||
|
|
||||||
|
```php |
||||||
|
function main() { |
||||||
|
// Set console to UTF-8 |
||||||
|
exec('chcp 65001 > 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... |
||||||
|
``` |
||||||
@ -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 |
||||||
|
<?php |
||||||
|
// 只声明函数签名,不包含实现 |
||||||
|
function messagebox(int $hWnd, string $text, string $caption, int $uType): int {} |
||||||
|
``` |
||||||
|
|
||||||
|
**2. C++ 实现文件** (`cpp-src/winapi.cc`): |
||||||
|
```cpp |
||||||
|
#include <phpx.h> |
||||||
|
#include <windows.h> |
||||||
|
|
||||||
|
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 |
||||||
|
<?php |
||||||
|
// 直接调用,无需额外声明 |
||||||
|
function main() { |
||||||
|
$result = messagebox(0, "Hello!", "Title", 0); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### 最简单的版本 (main.php) |
||||||
|
|
||||||
|
直接使用 Windows API(需要编译器支持原生函数声明): |
||||||
|
|
||||||
|
```php |
||||||
|
#[NativeFunction] |
||||||
|
function MessageBox(int $hWnd, string $lpText, string $lpCaption, int $uType): int {} |
||||||
|
|
||||||
|
function main() { |
||||||
|
MessageBox(0, "Hello World!", "标题", 0); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 扩展:创建完整窗口 |
||||||
|
|
||||||
|
要创建真正的 Windows 窗口(而不是消息框),需要: |
||||||
|
|
||||||
|
1. **注册窗口类** (WNDCLASS) |
||||||
|
2. **实现窗口过程函数** (WindowProc) |
||||||
|
3. **创建消息循环** (GetMessage/TranslateMessage/DispatchMessage) |
||||||
|
|
||||||
|
这些功能需要在 C++ 层实现,因为涉及到回调函数和复杂的 Windows 数据结构。 |
||||||
|
|
||||||
|
## 注意事项 |
||||||
|
|
||||||
|
- Windows API 函数需要通过 `#[NativeFunction]` 声明 |
||||||
|
- 复杂的 Windows API 建议在 C++ 层封装 |
||||||
|
- 编译时需要链接 Windows 系统库(user32.lib, gdi32.lib 等) |
||||||
|
- 程序必须是 `bin` 模式才能创建图形界面 |
||||||
|
|
||||||
|
## 参考 |
||||||
|
|
||||||
|
- [Windows API 文档](https://docs.microsoft.com/en-us/windows/win32/api/) |
||||||
|
- [PHPX 编译器文档](../../docs/README.md) |
||||||
|
- [examples/prime](../prime) - 混合 PHP 和 C++ 的示例 |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
#include <phpx.h> |
||||||
|
#include <windows.h> |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Windows API wrapper function declarations (stub) |
||||||
|
* These functions are implemented in C++, PHP layer only declares them |
||||||
|
*/ |
||||||
|
|
||||||
|
// Show message box |
||||||
|
function messagebox(int $hWnd, string $text, string $caption, int $uType): int {} |
||||||
|
|
||||||
|
// Get module handle |
||||||
|
function get_module_handle(string $moduleName): int {} |
||||||
|
|
||||||
|
// Create window |
||||||
|
function create_window(string $className, string $windowName, int $style, int $x, int $y, int $width, int $height): int {} |
||||||
|
|
||||||
|
// Show window |
||||||
|
function show_window(int $hWnd, int $cmdShow): bool {} |
||||||
|
|
||||||
|
// Update window |
||||||
|
function update_window(int $hWnd): bool {} |
||||||
|
|
||||||
|
// Exit message loop |
||||||
|
function post_quit_message(int $exitCode): void {} |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Win32 Hello World - Using C++ helper functions |
||||||
|
* This is the simplest Windows GUI program example |
||||||
|
* |
||||||
|
* Note: C++ functions are declared in cpp-src/winapi.stub.php and implemented in cpp-src/winapi.cc |
||||||
|
*/ |
||||||
|
|
||||||
|
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"; |
||||||
|
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"; |
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Win32 Window Hello World Example |
||||||
|
* Demonstrates how to create Windows GUI programs with PHPX compiler |
||||||
|
*/ |
||||||
|
|
||||||
|
// Declare external C functions (Windows API) |
||||||
|
#[NativeFunction] |
||||||
|
function CreateWindowEx( |
||||||
|
int $dwExStyle, |
||||||
|
string $lpClassName, |
||||||
|
string $lpWindowName, |
||||||
|
int $dwStyle, |
||||||
|
int $x, |
||||||
|
int $y, |
||||||
|
int $nWidth, |
||||||
|
int $nHeight, |
||||||
|
int $hWndParent, |
||||||
|
int $hMenu, |
||||||
|
int $hInstance, |
||||||
|
int $lpParam |
||||||
|
): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function ShowWindow(int $hWnd, int $nCmdShow): bool {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function UpdateWindow(int $hWnd): bool {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function GetMessage(array &$lpMsg, int $hWnd, int $wMsgFilterMin, int $wMsgFilterMax): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function TranslateMessage(array $lpMsg): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function DispatchMessage(array $lpMsg): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function DefWindowProc(int $hWnd, int $Msg, int $wParam, int $lParam): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function PostQuitMessage(int $nExitCode): void {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function MessageBox(int $hWnd, string $lpText, string $lpCaption, int $uType): int {} |
||||||
|
|
||||||
|
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'); |
||||||
|
} |
||||||
|
|
||||||
|
// Show a simple message box |
||||||
|
$result = MessageBox(0, "Hello from PHP Compiler!\n\n这是一个使用 PHPX 编译器创建的 Windows 程序。", "Hello World", 0); |
||||||
|
|
||||||
|
echo "消息框返回值: " . $result . "\n"; |
||||||
|
echo "程序结束。\n"; |
||||||
|
} |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
name: win32-hello |
||||||
|
version: 0.0.1 |
||||||
|
mode: bin |
||||||
|
sources: |
||||||
|
- hello-win.php |
||||||
|
- ./cpp-src |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Win32 Complete Window Example |
||||||
|
* Create a real Windows window with message loop |
||||||
|
*/ |
||||||
|
|
||||||
|
// Windows constant definitions |
||||||
|
define('WS_OVERLAPPEDWINDOW', 0x00CF0000); |
||||||
|
define('CW_USEDEFAULT', 0x80000000); |
||||||
|
define('SW_SHOW', 5); |
||||||
|
define('WM_DESTROY', 0x0002); |
||||||
|
define('MB_OK', 0x00000000); |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function RegisterClass(array $lpWndClass): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function CreateWindowEx( |
||||||
|
int $dwExStyle, |
||||||
|
string $lpClassName, |
||||||
|
string $lpWindowName, |
||||||
|
int $dwStyle, |
||||||
|
int $x, |
||||||
|
int $y, |
||||||
|
int $nWidth, |
||||||
|
int $nHeight, |
||||||
|
int $hWndParent, |
||||||
|
int $hMenu, |
||||||
|
int $hInstance, |
||||||
|
int $lpParam |
||||||
|
): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function ShowWindow(int $hWnd, int $nCmdShow): bool {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function UpdateWindow(int $hWnd): bool {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function GetMessage(array &$lpMsg, int $hWnd, int $wMsgFilterMin, int $wMsgFilterMax): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function TranslateMessage(array $lpMsg): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function DispatchMessage(array $lpMsg): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function DefWindowProc(int $hWnd, int $Msg, int $wParam, int $lParam): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function PostQuitMessage(int $nExitCode): void {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function MessageBox(int $hWnd, string $lpText, string $lpCaption, int $uType): int {} |
||||||
|
|
||||||
|
#[NativeFunction] |
||||||
|
function GetModuleHandle(string $lpModuleName): int {} |
||||||
|
|
||||||
|
// Window procedure function (simplified version, actually needs C++ implementation) |
||||||
|
function WindowProc(int $hWnd, int $Msg, int $wParam, int $lParam): int |
||||||
|
{ |
||||||
|
if ($Msg === WM_DESTROY) { |
||||||
|
PostQuitMessage(0); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return DefWindowProc($hWnd, $Msg, $wParam, $lParam); |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
echo "Win32 Hello World Program starting...\n"; |
||||||
|
|
||||||
|
// Show message box (simplest way) |
||||||
|
$result = MessageBox( |
||||||
|
0, |
||||||
|
"Hello from PHP Compiler!\n\nWelcome to use PHPX compiler to create Windows applications.", |
||||||
|
"Hello World", |
||||||
|
MB_OK |
||||||
|
); |
||||||
|
|
||||||
|
echo "Message box closed, return value: " . $result . "\n"; |
||||||
|
echo "Program exited normally.\n"; |
||||||
|
} |
||||||
Loading…
Reference in new issue