- 添加 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