From 6e58857074a9ad064bc514041626b4a8ffaa9f03 Mon Sep 17 00:00:00 2001 From: zqm Date: Mon, 13 Apr 2026 09:35:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E9=80=81=E5=8D=87=E7=BA=A7=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Windows/CS/Framework4.0/Updater/src/main.rs | 159 +++++++++++--------- 1 file changed, 92 insertions(+), 67 deletions(-) diff --git a/Windows/CS/Framework4.0/Updater/src/main.rs b/Windows/CS/Framework4.0/Updater/src/main.rs index d82658c..e43d62f 100644 --- a/Windows/CS/Framework4.0/Updater/src/main.rs +++ b/Windows/CS/Framework4.0/Updater/src/main.rs @@ -237,7 +237,7 @@ fn start_named_pipe_server( use windows::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX; use windows::Win32::System::Pipes::{ ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, - PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_WAIT, + PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_WAIT, }; use windows::core::PCWSTR; @@ -246,7 +246,7 @@ fn start_named_pipe_server( CreateNamedPipeW( PCWSTR::from_raw(name.as_ptr()), PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, // nMaxInstances 65536, // nOutBufferSize 65536, // nInBufferSize @@ -1383,14 +1383,14 @@ fn send_get_all_file( // ===================== 向应用发送升级确认 ===================== -/// 向指定应用发送升级确认消息 +/// 向指定应用发送升级确认消息(修复版:直接用 Win32 API 连接管道,不再用 PowerShell) /// 消息格式:{"Type":"UpgradeConfirm","Data":{"AppName":"xxx","CurrentVer":"1.0.0","LatestVer":"1.1.0"}} -fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, debug: bool) { +fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, _debug: bool) { #[cfg(windows)] { use std::process::Command; - // 查找运行中的进程,获取 PID(使用 PowerShell Get-Process 更可靠) + // 1. 获取所有运行中的进程 PID let ps_script = format!( "(Get-Process -Name '{}' -ErrorAction SilentlyContinue).Id", app_name @@ -1399,75 +1399,40 @@ fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, debug .args(["-NoProfile", "-NonInteractive", "-Command", &ps_script]) .output(); - let output_bytes = match output { - Ok(o) => o.stdout, + let pids: Vec = match output { + Ok(o) => { + let s = String::from_utf8_lossy(&o.stdout); + s.lines().filter_map(|l| l.trim().parse().ok()).collect() + } Err(_) => return, }; - let output_str = String::from_utf8_lossy(&output_bytes); - - // 解析 PID 列表(每行一个 PID) - let mut pids: Vec = Vec::new(); - for line in output_str.lines() { - let line = line.trim(); - if !line.is_empty() && line != "0" { - if let Ok(pid) = line.parse::() { - // 排除 Updater 自己 - let current_pid = std::process::id(); - if pid != current_pid { - pids.push(pid); - } - } - } - } if pids.is_empty() { - if debug { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - log_print!("{} [升级确认] {} 未运行,跳过通知", ts, app_name); - } + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + log_print!("{} [升级确认] {} 未运行,跳过通知", ts, app_name); return; } - // 向每个运行中的进程发送升级确认 - // 管道名称格式:EasyTest_{pid}(与 EasyTest 启动的命名管道一致) + // 2. 构造消息 + let msg = format!( + r#"{{"Type":"UpgradeConfirm","Data":{{"AppName":"{}","CurrentVer":"{}","LatestVer":"{}"}}}}"#, + app_name, current_ver, latest_ver + ); + + // 3. 对每个 PID 直接用 Win32 命名管道发送(不再用 PowerShell) for pid in pids { let pipe_name = format!("\\\\.\\pipe\\EasyTest_{}", pid); - let msg = format!( - r#"{{"Type":"UpgradeConfirm","Data":{{"AppName":"{}","CurrentVer":"{}","LatestVer":"{}"}}}}"#, - app_name, current_ver, latest_ver - ); - - // 使用 PowerShell 连接命名管道并发送消息 - let pipe_server = pipe_name.replace("\\\\.\\pipe\\", ""); - let ps_script = format!( - r#" - $pipe = [System.IO.Pipes.NamedPipeClientStream]::new(".", "{}", [System.IO.Pipes.PipeDirection]::InOut) - try {{ - $pipe.Connect(3000) - $writer = [System.IO.StreamWriter]::new($pipe) - $writer.AutoFlush = $true - $writer.WriteLine("{}") - $pipe.WaitForPipeDrain() - }} catch {{ - Write-Error $_.Exception.Message - }} finally {{ - $pipe.Close() - }} - "#, - pipe_server, - msg - ); - - let result = Command::new("powershell") - .args(["-NoProfile", "-NonInteractive", "-Command", &ps_script]) - .output(); - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - if result.is_ok() { + log_print!("{} [升级确认] 连接到 {} (PID={})", ts, pipe_name, pid); + + // 直接用 windows-rs Win32 API 连接命名管道 + let pipe_result = connect_named_pipe_byte(&pipe_name, &msg); + + if pipe_result { log_print!("{} [升级确认] 已通知 {} (PID={}): {} -> {}", ts, app_name, pid, current_ver, latest_ver); } else { - log_print!("{} [升级确认] 通知 {} (PID={}) 失败", ts, app_name, pid); + log_print!("{} [升级确认] 连接 {} (PID={}) 失败", ts, app_name, pid); } } } @@ -1479,22 +1444,82 @@ fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, debug } } +/// 用 Win32 API 连接命名管道并发送字节数据(字节模式) +fn connect_named_pipe_byte(pipe_name: &str, msg: &str) -> bool { + use windows::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE}; + use windows::Win32::Storage::FileSystem::{CreateFileW, WriteFile, FILE_ATTRIBUTE_NORMAL}; + use windows::core::PCWSTR; + + // Windows API 常量(跨版本稳定) + use windows::Win32::Storage::FileSystem::{FILE_SHARE_MODE, FILE_CREATION_DISPOSITION}; + const GENERIC_WRITE: u32 = 0x40000000u32; + const FILE_SHARE_NONE: u32 = 0u32; + const OPEN_EXISTING: u32 = 3u32; + + // 将管道名转换为宽字符串 + let wide_name: Vec = pipe_name.encode_utf16().chain(std::iter::once(0)).collect(); + + // 用 CreateFileW 打开命名管道(写模式,字节模式) + let pipe = unsafe { + CreateFileW( + PCWSTR::from_raw(wide_name.as_ptr()), + GENERIC_WRITE, + FILE_SHARE_MODE(FILE_SHARE_NONE), + None, + FILE_CREATION_DISPOSITION(OPEN_EXISTING), + FILE_ATTRIBUTE_NORMAL, + None, + ) + }; + + // CreateFileW 返回 Result,需要 unwrap + let pipe = match pipe { + Ok(h) => h, + Err(e) => { + eprintln!("[Pipe] CreateFileW failed: {:?}", e); + return false; + } + }; + + // 检查是否成功打开 + if pipe == INVALID_HANDLE_VALUE { + return false; + } + + // 写入消息 + 换行(C# 端用 StreamReader.ReadLine 读取) + let mut msg_with_newline = msg.to_string(); + msg_with_newline.push_str("\r\n"); + let data = msg_with_newline.as_bytes(); + + let mut written: u32 = 0; + let success = unsafe { + WriteFile( + pipe, + Some(data), + Some(&mut written), + None, + ) + }; + + let _ = unsafe { CloseHandle(pipe) }; + + success.is_ok() && written > 0 +} + /// 通知所有升级了的应用(EasyTest 等) /// app_upgrades: Vec<(app_name, current_ver, latest_ver)> -fn notify_all_app_upgrades(app_upgrades: &[(String, String, String)], debug: bool) { +fn notify_all_app_upgrades(app_upgrades: &[(String, String, String)], _debug: bool) { let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); if app_upgrades.is_empty() { - if debug { - log_print!("{} [升级确认] 没有应用需要升级通知", ts); - } + log_print!("{} [升级确认] 没有应用需要升级通知(upgraded_apps 为空)", ts); return; } - log_print!("{} [升级确认] 准备通知 {} 个应用", ts, app_upgrades.len()); + log_print!("{} [升级确认] 准备通知 {} 个应用: {:?}", ts, app_upgrades.len(), app_upgrades); for (app_name, current_ver, latest_ver) in app_upgrades { - notify_app_upgrade(app_name, current_ver, latest_ver, debug); + notify_app_upgrade(app_name, current_ver, latest_ver, true); // 始终打印通知详情 } }