From 1a32a59635aac62f4a78718dd7d3d90fb6d15f5c Mon Sep 17 00:00:00 2001 From: "949036910@qq.com" <> Date: Thu, 28 May 2026 08:47:06 +0800 Subject: [PATCH] 22 --- src-tauri/src/nodejs.rs | 73 ++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/src-tauri/src/nodejs.rs b/src-tauri/src/nodejs.rs index ee62115..f9ccc2d 100644 --- a/src-tauri/src/nodejs.rs +++ b/src-tauri/src/nodejs.rs @@ -41,6 +41,17 @@ static RE_REQUIRE: Lazy = const INLINE_ENTRY: &str = "__inline_entry__.js"; +/// Windows:子进程不创建可见控制台窗口(避免运行 node.exe 时闪黑框)。 +#[cfg(windows)] +fn hide_console_window(cmd: &mut Command) { + use std::os::windows::process::CommandExt; + const CREATE_NO_WINDOW: u32 = 0x0800_0000; + cmd.as_std_mut().creation_flags(CREATE_NO_WINDOW); +} + +#[cfg(not(windows))] +fn hide_console_window(_cmd: &mut Command) {} + #[derive(Debug, Error)] pub enum NodeError { #[error("node.exe 未找到(请检查 resources/nodejs/ 是否存在)")] @@ -125,6 +136,13 @@ fn build_file_runner(entry_script: &str) -> String { }}; }})(); +/** Windows 管道子进程:write 后立刻 process.exit 会触发 libuv UV_HANDLE_CLOSING 断言 */ +function writeAsync(stream, chunk) {{ + return new Promise((resolve, reject) => {{ + stream.write(chunk, (err) => (err ? reject(err) : resolve())); + }}); +}} + (async () => {{ let raw = ""; process.stdin.setEncoding("utf8"); @@ -134,29 +152,45 @@ fn build_file_runner(entry_script: &str) -> String { require("./{entry_script}"); if (typeof globalThis.__nodejsMain !== "function") {{ - process.stderr.write( - "__EVT__" + JSON.stringify({{ - type: "log", - level: "error", - msg: "脚本未注册 globalThis.__nodejsMain", - }}) + "\n" + await writeAsync( + process.stderr, + "__EVT__" + + JSON.stringify({{ + type: "log", + level: "error", + msg: "脚本未注册 globalThis.__nodejsMain", + }}) + + "\n" ); - process.exit(1); + process.exitCode = 1; + return; }} try {{ const result = await globalThis.__nodejsMain(params); - process.stdout.write( - "__RESULT__" + JSON.stringify(result === undefined ? null : result) + "\n" + await writeAsync( + process.stdout, + "__RESULT__" + + JSON.stringify(result === undefined ? null : result) + + "\n" ); - process.exit(0); }} catch (e) {{ const msg = (e && (e.stack || e.message)) || String(e); - process.stderr.write( + await writeAsync( + process.stderr, "__EVT__" + JSON.stringify({{ type: "log", level: "error", msg }}) + "\n" ); - process.exit(2); + process.exitCode = 2; }} -}})(); +}})().catch(async (e) => {{ + const msg = (e && (e.stack || e.message)) || String(e); + try {{ + await writeAsync( + process.stderr, + "__EVT__" + JSON.stringify({{ type: "log", level: "error", msg }}) + "\n" + ); + }} catch (_) {{}} + process.exitCode = 1; +}}); "# ) } @@ -369,6 +403,7 @@ async fn run_script_bundle( if let Some(dir) = crate::cover_python::locate_cover_scripts_dir() { cmd.env("AICLIENT_COVER_SCRIPTS_DIR", dir); } + hide_console_window(&mut cmd); let mut child = cmd.spawn()?; // 把 params JSON 灌进 stdin @@ -456,9 +491,15 @@ async fn run_script_bundle( result }); - let status = child.wait().await?; - let stderr_tail = stderr_task.await.unwrap_or_default(); - let result_line = stdout_task.await.unwrap_or(None); + // 与 stdout/stderr 读取并行等待,避免子进程在管道未排空时退出(Windows libuv) + let (status, stderr_tail, result_line) = tokio::join!( + child.wait(), + stderr_task, + stdout_task, + ); + let status = status?; + let stderr_tail = stderr_tail.unwrap_or_default(); + let result_line = result_line.unwrap_or(None); let _ = tokio::fs::remove_dir_all(&bundle_dir).await;