diff --git a/工作OKRs/25.9-11 OKR.canvas b/工作OKRs/25.9-11 OKR.canvas index 8f54d64..c215cfe 100644 --- a/工作OKRs/25.9-11 OKR.canvas +++ b/工作OKRs/25.9-11 OKR.canvas @@ -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":[] } \ No newline at end of file diff --git a/未命名.md b/未命名.md new file mode 100644 index 0000000..7b06a3e --- /dev/null +++ b/未命名.md @@ -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::().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, + data: web::Data>, +) -> impl Responder { + let app_state = data.lock().unwrap(); + + // 从已加载的库中获取函数符号 + // 使用 .get() 每次调用都是安全的,因为它会重新查找符号 + match unsafe { app_state.lib.get::(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 { + 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::().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 位代码的互操作问题。 \ No newline at end of file