vault backup: 2025-09-15 14:15:17
This commit is contained in:
parent
cd99ea55f0
commit
d3811de950
@ -4,11 +4,12 @@
|
||||
{"id":"a4eaccbbfadaaf17","type":"text","text":"# 目标:\n多体模块完善 线性化模块开发\n### 每周盘点一下它们\n\n\n关键结果:多体动力学建模原理、建模方法、线性化原理掌握 (9/10)\n\n关键结果:风机多体动力学文献调研情况完成 (5.5/10)\n关键结果:目标工况测试、稳态工况对比 (5/10)","x":-76,"y":-803,"width":456,"height":457},
|
||||
{"id":"d2c5e076ba6cf7d7","type":"text","text":"# 推进计划\n未来四周计划推进的重要事情\n\n文献调研启动\n\n建模重新推导\n\n\n","x":-600,"y":-306,"width":456,"height":347},
|
||||
{"id":"82708a439812fdc7","type":"text","text":"# 10月已完成\n\n","x":-220,"y":134,"width":440,"height":560},
|
||||
{"id":"505acb3e6b119076","type":"text","text":"# 9月已完成\n","x":-700,"y":134,"width":440,"height":560},
|
||||
{"id":"505acb3e6b119076","type":"text","text":"# 9月已完成\n\nP1 湍流 气动 多体 控制联调 done\n- 5mw 通了\n\t- 纯叶片变形\n\t- 纯塔架变形\n\t- 叶片+塔架变形 ","x":-700,"y":134,"width":440,"height":560},
|
||||
{"id":"30cb7486dc4e224c","type":"text","text":"# 11月已完成\n\n\n\n","x":260,"y":134,"width":440,"height":560},
|
||||
{"id":"c18d25521d773705","type":"text","text":"# 计划\n这周要做的3~5件重要的事情,这些事情能有效推进实现OKR。\n\nP1 必须做。P2 应该做\n\n\nP2 柔性部件 叶片、塔架变形算法 主线\n- 变形体动力学 简略看看ing\n- 柔性梁弯曲变形振动学习,主线 \n\t- 广义质量 刚度矩阵及含义\n\t\n- 梳理bladed动力学框架\n\t- 子结构文献阅读\n\t- 叶片模型建模 done\n- 共旋方法学习\n- DTU 变形量计算方法学习\n\n\nP1 线性化方法编写 搁置\n\nP1 气动、多体、控制、水动联调\nP1 湍流 气动 多体 控制联调 \n- 5mw 通了\n\t- 纯叶片变形\n\t- 纯塔架变形\n\t- 叶片+塔架变形\n- 15mw呢 yaml多个模块都需要支持\n- 更换湍流风\nP2 停机工况等调试\n\nP1 bladed对比--稳态,产出报告\n- 模态对比 两种描述方法不同,bladed方向更多,x y z deflection, x y z rotation,不好对比\n- 气动对比 aerodynamic info 轴向切向诱导因子,根部,尖部差距较大\n- 稳态变形量对比\n- 稳态变形量对比 -- steady power production loading、steady parked loading\n\nP1 稳态工况前端对接\n- 是否拆分成单独的bin,等待气动完成后开始\n- 如何接收参数 配置文件 \n\n\nP2 如何优雅的存储、输出结果。\nP2 yaw 自由度再bug确认 已知原理了\n","x":-597,"y":-803,"width":453,"height":457},
|
||||
{"id":"c18d25521d773705","type":"text","text":"# 计划\n这周要做的3~5件重要的事情,这些事情能有效推进实现OKR。\n\nP1 必须做。P2 应该做\n\n\nP2 柔性部件 叶片、塔架变形算法 主线\n- 变形体动力学 简略看看ing\n- 柔性梁弯曲变形振动学习,主线 \n\t- 广义质量 刚度矩阵及含义\n\t\n- 梳理bladed动力学框架\n\t- 子结构文献阅读\n\t- 叶片模型建模 done\n- 共旋方法学习\n- DTU 变形量计算方法学习\n\n\nP1 线性化方法编写 搁置\n\nP1 气动、多体、控制、水动联调\nP1 湍流 气动 多体 控制联调 \n- 5mw结果与Bladed对比\n- 15mw呢 yaml多个模块都需要支持\n- 更换湍流风\n- dll 32位兼容 - 江\nP2 停机工况等调试\n\nP1 bladed对比--稳态,产出报告\n- 模态对比 两种描述方法不同,bladed方向更多,x y z deflection, x y z rotation,不好对比\n- 气动对比 aerodynamic info 轴向切向诱导因子,根部,尖部差距较大\n- 稳态变形量对比\n- 稳态变形量对比 -- steady power production loading、steady parked loading\n\nP1 稳态工况前端对接\n- 是否拆分成单独的bin,等待气动完成后开始\n- 如何接收参数 配置文件 \n\n\nP2 如何优雅的存储、输出结果。\nP2 yaw 自由度再bug确认 已知原理了\n","x":-597,"y":-803,"width":453,"height":457},
|
||||
{"id":"86ab96a25a3bf82e","type":"text","text":" 湍流风+ 控制的联调,bladed也算一个算例\n- 加水动的联调\n- 8月份底完成这两个\n- 9月份完成停机等工况测试\n- 10月份明阳实际机型测试","x":580,"y":-803,"width":480,"height":220},
|
||||
{"id":"e355f33c92cf18ea","type":"text","text":"9月份定常计算对接前端\n非定常测试完也对接前端","x":580,"y":-500,"width":480,"height":100}
|
||||
{"id":"e355f33c92cf18ea","type":"text","text":"9月份定常计算对接前端\n非定常测试完也对接前端","x":580,"y":-500,"width":480,"height":100},
|
||||
{"id":"859e6853b7f1b92b","x":1200,"y":-803,"width":320,"height":110,"type":"text","text":"年底考核:\n专利\n线性化模块"}
|
||||
],
|
||||
"edges":[]
|
||||
}
|
458
未命名.md
Normal file
458
未命名.md
Normal file
@ -0,0 +1,458 @@
|
||||
### USER
|
||||
rust 64位代码,通过unsafe调用discon.dll,现在discon.dll是64位,程序可以运行,discon.dll改成32位,提示无法加载DISCON.dll!错误: %1 is not a valid Win32 application. (0x800700C1),如何兼容32位discon.dll
|
||||
|
||||
### ASSISTANT
|
||||
好的,这是一个非常经典的问题,涉及到操作系统进程的体系结构(Architecture)隔离。
|
||||
|
||||
你遇到的错误 `... %1 is not a valid Win32 application. (0x800700C1)` 是 Windows 操作系统的底层限制,它明确地告诉你:**一个 64 位 (x86_64) 的进程无法直接加载一个 32 位 (i686) 的 DLL。**
|
||||
|
||||
反之亦然,一个 32 位的进程也无法加载一个 64 位的 DLL。它们的内存地址空间、指针大小、寄存器用法和调用约定都完全不同,操作系统从设计上就禁止了这种直接的混合加载。
|
||||
|
||||
因此,你的 64 位 Rust 程序尝试加载 32 位 `discon.dll` 时,Windows 加载器会立即失败,并返回这个错误。
|
||||
|
||||
要解决这个问题,你不能让 64 位进程“兼容”32 位 DLL,而是要改变你的策略。以下是两种主要的解决方案,从最简单到最复杂排列。
|
||||
|
||||
### 方法一:将您的 Rust 程序编译为 32 位 (最简单、最推荐)
|
||||
|
||||
如果你的 Rust 程序没有特殊要求必须是 64 位(例如,需要超过 4GB 的内存),那么最直接、最简单、性能最好的方法就是将你的 Rust 程序也编译成 32 位版本。这样,32 位的程序就可以顺利加载 32 位的 DLL。
|
||||
|
||||
**操作步骤如下:**
|
||||
|
||||
1. **添加 32 位编译目标:**
|
||||
打开你的命令行工具(如 PowerShell 或 CMD),运行以下命令来为 Rust 添加 32 位 Windows 的编译目标。通常我们使用 MSVC 工具链。
|
||||
抱歉,上一步的 `rustup` 命令是我提供给您在您自己的计算机上运行的指令,而不是在我的执行环境中运行的代码,因此出现了错误。请忽略该错误输出。
|
||||
|
||||
以下是完整的解决方案:
|
||||
|
||||
---
|
||||
|
||||
好的,这是一个非常经典的问题,涉及到操作系统进程的体系结构(Architecture)隔离。
|
||||
|
||||
你遇到的错误 `... %1 is not a valid Win32 application. (0x800700C1)` 是 Windows 操作系统的底层限制,它明确地告诉你:**一个 64 位 (x86_64) 的进程无法直接加载一个 32 位 (i686) 的 DLL。**
|
||||
|
||||
反之亦然,一个 32 位的进程也无法加载一个 64 位的 DLL。它们的内存地址空间、指针大小、寄存器用法和调用约定都完全不同,操作系统从设计上就禁止了这种直接的混合加载。
|
||||
|
||||
因此,你的 64 位 Rust 程序尝试加载 32 位 `discon.dll` 时,Windows 加载器会立即失败,并返回这个错误。
|
||||
|
||||
要解决这个问题,你不能让 64 位进程“兼容”32 位 DLL,而是要改变你的策略。以下是两种主要的解决方案,从最简单到最复杂排列。
|
||||
|
||||
### 方法一:将您的 Rust 程序编译为 32 位 (最简单、最推荐)
|
||||
|
||||
如果你的 Rust 程序没有特殊要求必须是 64 位(例如,需要超过 4GB 的内存),那么最直接、最简单、性能最好的方法就是将你的 Rust 程序也编译成 32 位版本。这样,32 位的程序就可以顺利加载 32 位的 DLL。
|
||||
|
||||
**操作步骤如下:**
|
||||
|
||||
1. **添加 32 位编译目标:**
|
||||
打开你的命令行工具(如 PowerShell 或 CMD),运行以下命令来为 Rust 添加 32 位 Windows 的编译目标。通常我们使用 MSVC 工具链。
|
||||
```shell
|
||||
rustup target add i686-pc-windows-msvc
|
||||
```
|
||||
*(你需要预先安装 Visual Studio 的 C++ 构建工具,并确保选择了 32 位编译支持)*
|
||||
|
||||
2. **以 32 位目标进行编译:**
|
||||
使用 `--target` 参数来构建你的项目。
|
||||
```shell
|
||||
cargo build --target=i686-pc-windows-msvc
|
||||
```
|
||||
如果需要发布版本,则:
|
||||
```shell
|
||||
cargo build --release --target=i686-pc-windows-msvc
|
||||
```
|
||||
编译成功后,在 `target/i686-pc-windows-msvc/debug/` 或 `release/` 目录下找到的 `.exe` 文件就是 32 位的。将这个 32 位的 exe 和 32 位的 `discon.dll` 放在一起,就可以正常运行了。
|
||||
|
||||
### 方法二:使用进程间通信 (IPC) (当主程序必须为 64 位时)
|
||||
|
||||
在某些情况下,你的主程序必须是 64 位的(比如它依赖于其他 64 位的库,或者需要处理大量数据)。在这种情况下,你就不能直接加载 32 位 DLL。
|
||||
|
||||
解决方案是创建一个**“帮助程序”(Helper Process)**。
|
||||
|
||||
这个架构看起来是这样的:
|
||||
1. **你的主程序 (64-bit, Rust):** 保持 64 位。
|
||||
2. **帮助程序 (32-bit):** 一个非常简单的独立小程序,它的唯一职责就是加载 32 位的 `discon.dll`,并调用其中的函数。你可以用任何能够编译成 32 位的语言来写这个帮助程序,包括 Rust 自己!
|
||||
3. **通信机制 (IPC):** 主程序和帮助程序通过一种进程间通信(IPC)的方式来交换数据。
|
||||
|
||||
**实现流程:**
|
||||
|
||||
1. **创建 32 位帮助程序:**
|
||||
* 创建一个新的、简单的 Rust 项目 (e.g., `cargo new discon_helper`)。
|
||||
* 在这个新项目中,编写加载 32 位 `discon.dll` 并调用其功能的代码(和你现在主程序里的 `unsafe` 调用类似)。
|
||||
* 这个帮助程序需要能从外部接收指令(比如从标准输入 `stdin` 读取命令)并把结果输出到标准输出 `stdout`。
|
||||
* 使用方法一中提到的命令,将这个帮助程序编译成 32 位:`cargo build --release --target=i686-pc-windows-msvc`。
|
||||
|
||||
2. **在 64 位主程序中调用帮助程序:**
|
||||
* 在你的主程序中,使用 Rust 的 `std::process::Command` 来启动这个 32 位的帮助程序 (`discon_helper.exe`)。
|
||||
* 通过管道(Pipes)将需要传递给 DLL 函数的参数写入帮助程序的 `stdin`。
|
||||
* 从帮助程序的 `stdout` 读取 DLL 函数的执行结果。
|
||||
|
||||
**简单的 IPC 示例 (通过 Stdin/Stdout):**
|
||||
|
||||
**32 位帮助程序 (`discon_helper/src/main.rs`):**
|
||||
```rust
|
||||
// 伪代码,需要根据 discon.dll 的实际函数进行修改
|
||||
use std::io::{self, Read};
|
||||
|
||||
// 定义 DLL 中的函数签名
|
||||
#[link(name = "discon")]
|
||||
extern "C" {
|
||||
fn some_function_in_discon(input: i32) -> i32;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// 从标准输入读取数据
|
||||
let mut buffer = [0u8; 4]; // 假设输入是一个32位整数
|
||||
io::stdin().read_exact(&mut buffer).unwrap();
|
||||
let input_val = i32::from_le_bytes(buffer);
|
||||
|
||||
// 调用 DLL 函数
|
||||
let result = unsafe { some_function_in_discon(input_val) };
|
||||
|
||||
// 将结果打印到标准输出
|
||||
print!("{}", result);
|
||||
}
|
||||
```
|
||||
|
||||
**64 位主程序 (`main.rs`):**
|
||||
```rust
|
||||
// 伪代码
|
||||
use std::process::{Command, Stdio};
|
||||
use std::io::Write;
|
||||
|
||||
fn call_discon_via_helper(param: i32) -> i32 {
|
||||
let mut child = Command::new("path/to/discon_helper.exe")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Failed to start helper process");
|
||||
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
// 将参数写入帮助程序的标准输入
|
||||
stdin.write_all(¶m.to_le_bytes()).expect("Failed to write to stdin");
|
||||
|
||||
// stdin 被丢弃,管道关闭,帮助程序可以继续执行
|
||||
|
||||
let output = child.wait_with_output().expect("Failed to read stdout");
|
||||
|
||||
let result_str = String::from_utf8(output.stdout).unwrap();
|
||||
result_str.parse::<i32>().unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let result = call_discon_via_helper(123);
|
||||
println!("Result from 32-bit DLL: {}", result);
|
||||
}
|
||||
```
|
||||
|
||||
**其他更健壮的 IPC 方法:**
|
||||
* **本地 Web 服务:** 32 位帮助程序可以启动一个微型的 HTTP 服务器 (如使用 `actix-web` 或 `tiny_http`),64 位主程序通过发送 HTTP 请求到 `localhost` 来调用功能。这种方式扩展性好,易于调试。
|
||||
* **命名管道 (Named Pipes):** Windows 提供的一种高效 IPC 机制,适合需要持续、双向通信的场景。
|
||||
* **COM (Component Object Model):** 这是 Windows 平台最重量级也最强大的 IPC/FFI 解决方案,但实现起来也最复杂。
|
||||
|
||||
### 如何选择?
|
||||
|
||||
1. **首选方法一:** 如果没有硬性要求,**总是优先选择将你的 Rust 程序编译成 32 位**。这是最简单、最高效、最不容易出错的方案。
|
||||
2. **检查是否有 64 位 DLL:** 询问 `discon.dll` 的提供方,他们是否能提供 64 位版本?这能从根本上解决问题。
|
||||
3. **最后才考虑方法二:** 只有当你**必须**使用 64 位主程序,并且**绝对无法**获得 64 位版本的 DLL 时,才采用进程间通信(IPC)的方案。
|
||||
|
||||
综上所述,请先尝试使用 `cargo build --target=i686-pc-windows-msvc` 来解决你的问题。
|
||||
|
||||
### USER
|
||||
只有当你**必须**使用 64 位主程序,并且**绝对无法**获得 64 位版本的 DLL 时,才采用进程间通信(IPC)的方案。详细描述这个方案,我计划采用此路线
|
||||
|
||||
### ASSISTANT
|
||||
好的,非常棒的选择。采用进程间通信(IPC)是解决此类跨架构调用问题的标准工业级方案。它虽然比直接链接要复杂,但提供了极好的解耦和稳定性。
|
||||
|
||||
我们将使用一个非常现代且健壮的方法:**通过本地 HTTP 服务进行通信**。
|
||||
|
||||
* **64位主程序 (Client):** 你的主程序,作为 HTTP 客户端。
|
||||
* **32位帮助程序 (Server):** 一个轻量级的 Rust 程序,内嵌一个 HTTP 服务器,负责加载 32 位 `discon.dll` 并暴露其功能。
|
||||
|
||||
这种方法的优点是:
|
||||
* **调试简单:** 你可以独立运行和测试 32 位帮助程序,甚至可以用浏览器或 `curl` 这样的工具来调用它。
|
||||
* **数据清晰:** 使用 JSON 作为数据交换格式,结构清晰,易于扩展。
|
||||
* **跨语言:** 你的主程序甚至不一定需要是 Rust,任何能发 HTTP 请求的语言都可以与这个帮助程序通信。
|
||||
* **健壮性:** 网络通信有成熟的错误处理模式。
|
||||
|
||||
---
|
||||
|
||||
### 架构概览
|
||||
|
||||
```
|
||||
+---------------------------------+ (HTTP Request over localhost) +-----------------------------------------+
|
||||
| | POST http://127.0.0.1:8032/call_func | |
|
||||
| Your 64-bit Main App (Rust) | <-----------------------------------> | 32-bit Helper App (Rust + Actix-Web) |
|
||||
| (e.g., my_app.exe) | (JSON data request/response) | (e.g., discon_helper.exe) |
|
||||
| | | |
|
||||
+---------------------------------+ +-----------------------+-----------------+
|
||||
| (unsafe FFI call)
|
||||
V
|
||||
+-----------------+
|
||||
| 32-bit discon.dll |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第1部分:创建 32 位帮助程序 (HTTP 服务器)
|
||||
|
||||
这个程序是与 `discon.dll` 直接交互的桥梁。
|
||||
|
||||
#### 1.1. 项目设置
|
||||
|
||||
首先,创建一个新的 Rust 项目:```shell
|
||||
cargo new discon_helper
|
||||
cd discon_helper
|
||||
```
|
||||
|
||||
编辑 `Cargo.toml` 文件,添加以下依赖:
|
||||
* `actix-web`: 一个功能强大且易于使用的 Web 框架。
|
||||
* `serde`: 用于序列化和反序列化数据(JSON)。
|
||||
* `libloading`: 一种更安全、更灵活的加载动态链接库的方式。
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "discon_helper"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
libloading = "0.8"
|
||||
```
|
||||
|
||||
#### 1.2. 编写服务器代码
|
||||
|
||||
为了演示,我们假设 `discon.dll` 中有一个函数,签名如下:
|
||||
`int add_numbers(int a, int b);`
|
||||
|
||||
现在,编辑 `src/main.rs` 文件:
|
||||
|
||||
```rust
|
||||
use actix_web::{web, App, HttpServer, Responder, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use libloading::{Library, Symbol};
|
||||
use std::sync::Mutex;
|
||||
|
||||
// 1. 定义与 DLL 函数签名匹配的类型
|
||||
type AddNumbersFunc = unsafe extern "C" fn(a: i32, b: i32) -> i32;
|
||||
|
||||
// 2. 定义用于 HTTP 请求和响应的结构体
|
||||
// 这些结构体将在客户端和服务器之间通过 JSON 进行交换
|
||||
#[derive(Deserialize)]
|
||||
struct AddRequest {
|
||||
a: i32,
|
||||
b: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AddResponse {
|
||||
result: i32,
|
||||
}
|
||||
|
||||
// 3. 将 DLL 库的句柄和函数符号存储在 Actix 的应用状态中
|
||||
// 使用 Mutex 来确保线程安全,因为 Actix 会在多个线程中处理请求
|
||||
struct AppState {
|
||||
lib: Library,
|
||||
}
|
||||
|
||||
// 4. 定义处理 HTTP POST 请求的处理器
|
||||
async fn call_add_numbers(
|
||||
req: web::Json<AddRequest>,
|
||||
data: web::Data<Mutex<AppState>>,
|
||||
) -> impl Responder {
|
||||
let app_state = data.lock().unwrap();
|
||||
|
||||
// 从已加载的库中获取函数符号
|
||||
// 使用 .get() 每次调用都是安全的,因为它会重新查找符号
|
||||
match unsafe { app_state.lib.get::<AddNumbersFunc>(b"add_numbers\0") } {
|
||||
Ok(add_numbers) => {
|
||||
// 调用 DLL 函数
|
||||
let result = unsafe { add_numbers(req.a, req.b) };
|
||||
|
||||
// 返回成功的 JSON 响应
|
||||
HttpResponse::Ok().json(AddResponse { result })
|
||||
}
|
||||
Err(e) => {
|
||||
// 如果函数不存在,返回内部服务器错误
|
||||
HttpResponse::InternalServerError().body(format!("Failed to get function from DLL: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 主函数:加载 DLL 并启动服务器
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
println!("Starting 32-bit helper server...");
|
||||
|
||||
// 加载 discon.dll
|
||||
// 将 discon.dll 放在生成的 exe 文件旁边
|
||||
let lib = unsafe {
|
||||
Library::new("discon.dll")
|
||||
.expect("Failed to load discon.dll. Make sure it's next to the executable.")
|
||||
};
|
||||
|
||||
// 将库句柄封装在 Mutex 和 web::Data 中,以便在线程间共享
|
||||
let app_state = web::Data::new(Mutex::new(AppState { lib }));
|
||||
let port = 8032;
|
||||
|
||||
println!("Server listening on http://127.0.0.1:{}", port);
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(app_state.clone()) // 注册应用状态
|
||||
.route("/add", web::post().to(call_add_numbers)) // 定义路由
|
||||
})
|
||||
.bind(("127.0.0.1", port))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3. 编译为 32 位
|
||||
|
||||
```shell
|
||||
# (确保你已经运行了 rustup target add i686-pc-windows-msvc)
|
||||
cargo build --release --target=i686-pc-windows-msvc
|
||||
```
|
||||
编译成功后,你会在 `target/i686-pc-windows-msvc/release/` 目录下找到 `discon_helper.exe`。**将 32 位的 `discon.dll` 复制到这个目录中**。
|
||||
|
||||
---
|
||||
|
||||
### 第2部分:修改 64 位主程序 (HTTP 客户端)
|
||||
|
||||
现在,我们来修改你原来的 64 位程序,让它通过 HTTP 调用帮助程序。
|
||||
|
||||
#### 2.1. 项目设置
|
||||
|
||||
在你的 64 位主程序项目的 `Cargo.toml` 中,添加以下依赖:
|
||||
* `reqwest`: 一个强大且易于使用的 HTTP 客户端。
|
||||
* `tokio`: 一个异步运行时,`reqwest` 需要它。
|
||||
* `serde`: 同样用于序列化和反序列化。
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
```
|
||||
|
||||
#### 2.2. 编写客户端代码
|
||||
|
||||
现在,修改你的 64 位主程序的 `src/main.rs`。
|
||||
|
||||
```rust
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::process::{Command, Child}; // 用于管理帮助程序的生命周期
|
||||
|
||||
// 1. 定义与服务器端完全相同的请求和响应结构体
|
||||
#[derive(Serialize)]
|
||||
struct AddRequest {
|
||||
a: i32,
|
||||
b: i32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AddResponse {
|
||||
result: i32,
|
||||
}
|
||||
|
||||
// 2. 封装调用逻辑的异步函数
|
||||
async fn call_add_via_helper(client: &reqwest::Client, a: i32, b: i32) -> Result<i32, reqwest::Error> {
|
||||
let request_payload = AddRequest { a, b };
|
||||
|
||||
let response = client
|
||||
.post("http://127.0.0.1:8032/add")
|
||||
.json(&request_payload)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
// 检查服务器是否返回了成功状态
|
||||
if response.status().is_success() {
|
||||
let add_response = response.json::<AddResponse>().await?;
|
||||
Ok(add_response.result)
|
||||
} else {
|
||||
// 如果服务器返回错误,将其作为错误信息传递出去
|
||||
let error_text = response.text().await?;
|
||||
eprintln!("Error from helper: {}", error_text);
|
||||
// 在实际应用中,这里应该返回一个更具体的错误类型
|
||||
Err(reqwest::Error::from(std::io::Error::new(std::io::ErrorKind::Other, error_text)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// RAII 结构体,用于在程序退出时自动终止子进程
|
||||
struct ChildProcessGuard(Child);
|
||||
|
||||
impl Drop for ChildProcessGuard {
|
||||
fn drop(&mut self) {
|
||||
println!("Shutting down helper process...");
|
||||
if let Err(e) = self.0.kill() {
|
||||
eprintln!("Failed to kill helper process: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3. 主函数:启动帮助程序,然后调用它
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// 启动 32 位帮助程序
|
||||
// 假设 discon_helper.exe 在主程序的子目录 "helper" 中
|
||||
let helper_path = "./discon_helper.exe";
|
||||
let child = Command::new(helper_path)
|
||||
.spawn()
|
||||
.expect("Failed to start discon_helper.exe. Make sure it is in the correct path.");
|
||||
|
||||
// 使用 Guard 来确保即使 main 函数 panic,子进程也能被清理
|
||||
let _guard = ChildProcessGuard(child);
|
||||
|
||||
// 给帮助程序一点时间来启动服务器
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||
|
||||
println!("64-bit main application started.");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// 调用函数
|
||||
match call_add_via_helper(&client, 10, 20).await {
|
||||
Ok(result) => {
|
||||
println!("[Main App] The result from discon.dll is: {}", result);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[Main App] Failed to call helper: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 当 main 函数结束时,_guard 会被丢弃,其 drop 方法会被调用,从而杀死子进程
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3. 编译为 64 位
|
||||
|
||||
这个很简单,就是你平时的编译命令:
|
||||
```shell
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第3部分:部署和运行
|
||||
|
||||
1. **组织文件结构:**
|
||||
创建一个最终的部署文件夹,例如 `my_final_app`。
|
||||
```
|
||||
my_final_app/
|
||||
├── my_app.exe (你的 64 位主程序)
|
||||
├── discon_helper.exe (编译好的 32 位帮助程序)
|
||||
└── discon.dll (32 位 DLL 文件)
|
||||
```
|
||||
确保 `discon_helper.exe` 和 `discon.dll` 在同一个目录下。
|
||||
|
||||
2. **运行:**
|
||||
* 双击或从命令行运行 `my_app.exe`。
|
||||
* 它会首先启动 `discon_helper.exe`(你可能会看到一个一闪而过的控制台窗口)。
|
||||
* 然后,主程序会等待几秒钟,接着发送 HTTP 请求。
|
||||
* 你会看到主程序的控制台输出从 DLL 计算得到的结果。
|
||||
* 当你关闭主程序时,它会自动终止 `discon_helper.exe` 进程。
|
||||
|
||||
这个方案为你提供了一个稳定、可维护且功能强大的方式来解决 64 位和 32 位代码的互操作问题。
|
Loading…
x
Reference in New Issue
Block a user