From 34e60f789735c1e316f5af4653461f35897c473c Mon Sep 17 00:00:00 2001 From: zqm Date: Fri, 10 Apr 2026 16:29:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BD=93=E5=89=8D=E5=AE=9E=E7=8E=B0=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E6=94=AF=E6=8C=81=E6=89=80=E6=9C=89=E5=BA=94=E7=94=A8?= =?UTF-8?q?=EF=BC=8C=E5=8F=AA=E8=A6=81=E5=BA=94=E7=94=A8=E9=81=B5=E5=BE=AA?= =?UTF-8?q?=EF=BC=9A=201.=20=E5=91=BD=E5=90=8D=E7=AE=A1=E9=81=93=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=EF=BC=9A{=E5=BA=94=E7=94=A8=E5=90=8D}=5F{PID}=202.=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8E=A5=E6=94=B6=20{"Type":"UpgradeConfirm"?= =?UTF-8?q?,"Data":{...}}=20=E6=B6=88=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 | 129 ++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/Windows/CS/Framework4.0/Updater/src/main.rs b/Windows/CS/Framework4.0/Updater/src/main.rs index fa70568..76f78dc 100644 --- a/Windows/CS/Framework4.0/Updater/src/main.rs +++ b/Windows/CS/Framework4.0/Updater/src/main.rs @@ -111,6 +111,8 @@ struct UpdateContext { is_downloading: Mutex, /// 当前正在下载的文件的期望 MD5(DownloadComplete 校验用) current_download_md5: Mutex>, + /// 已升级的应用列表 (app_name, current_ver, latest_ver),下载完成后通知用 + upgraded_apps: Mutex>, } impl Default for UpdateContext { @@ -126,6 +128,7 @@ impl Default for UpdateContext { download_queue: Mutex::new(std::collections::VecDeque::new()), is_downloading: Mutex::new(false), current_download_md5: Mutex::new(None), + upgraded_apps: Mutex::new(Vec::new()), } } } @@ -1374,6 +1377,119 @@ fn send_get_all_file( sender.send(msg_str); } +// ===================== 向应用发送升级确认 ===================== + +/// 向指定应用发送升级确认消息 +/// 消息格式:{"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) { + #[cfg(windows)] + { + use std::process::Command; + + // 查找运行中的进程,获取 PID + let output = Command::new("tasklist") + .args(["/FI", &format!("IMAGENAME eq {}.exe", app_name), "/FO", "CSV"]) + .output(); + + let output_bytes = match output { + Ok(o) => o.stdout, + Err(_) => return, + }; + let output_str = String::from_utf8_lossy(&output_bytes); + let lines: Vec<&str> = output_str.lines().collect(); + + let mut pids: Vec = Vec::new(); + for line in lines { + if line.contains(&format!("\"{}\"", app_name)) { + if let Some(pid_part) = line.split(",").nth(1) { + if let Ok(pid) = pid_part.trim_matches('"').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); + } + return; + } + + // 向每个运行中的进程发送升级确认 + for pid in pids { + let pipe_name = format!("\\\\.\\pipe\\{}_{}", app_name, 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]::Out) + 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, app_name, pid, current_ver, latest_ver); + } else { + log_print!("{} [升级确认] 通知 {} (PID={}) 失败", ts, app_name, pid); + } + } + } + + #[cfg(not(windows))] + { + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + log_print!("{} [升级确认] 非 Windows 平台,跳过通知", ts); + } +} + +/// 通知所有升级了的应用(EasyTest 等) +/// app_upgrades: Vec<(app_name, current_ver, latest_ver)> +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); + } + return; + } + + log_print!("{} [升级确认] 准备通知 {} 个应用", ts, app_upgrades.len()); + + for (app_name, current_ver, latest_ver) in app_upgrades { + notify_app_upgrade(app_name, current_ver, latest_ver, debug); + } +} + /// 主动断连信号(用于在消息回调中请求优雅退出) /// - shutdown_tx: 从同步回调中发送,通知主循环主动断开 /// - disconnect_rx: 在主循环中等待 @@ -1651,6 +1767,7 @@ async fn run_updater(debug_mode: bool) -> bool { // 使用预存的版本(在候选发现阶段已从运行中进程读取) let candidates = ctx_clone.candidates.lock().unwrap().clone(); let mut apps_to_update = Vec::new(); + let mut upgraded_apps = Vec::new(); // (app_name, current_ver, latest_ver) for (app_name, local_version) in candidates { let exe_name = format!("{}\\{}.exe", app_name, app_name); @@ -1666,10 +1783,15 @@ async fn run_updater(debug_mode: bool) -> bool { if local_version == "0.0.0" || version_less_than(&local_version, server_version) { apps_to_update.push(app_name.clone()); + // 记录需要升级的应用信息(用于后续发送通知) + upgraded_apps.push((app_name.clone(), local_version.clone(), server_version.to_string())); } } } + // 保存升级应用列表 + *ctx_clone.upgraded_apps.lock().unwrap() = upgraded_apps; + if apps_to_update.is_empty() { // 没有需要更新的应用,所有阶段完成 log_print!("{} [阶段完成] 所有应用已是最新版本,等待下次检查...", ts); @@ -2229,6 +2351,13 @@ async fn run_updater(debug_mode: bool) -> bool { println!("Updater 本次运行结束"); } + // 在退出前,通知所有升级的应用(发送升级确认消息) + let upgraded_apps = ctx.upgraded_apps.lock().unwrap().clone(); + drop(ctx); // 释放 ctx 引用 + if !upgraded_apps.is_empty() { + notify_all_app_upgrades(&upgraded_apps, debug_mode); + } + // 返回本次运行是否更新了 Updater.exe 自身(只有 Updater 自身更新了才需要退出重启) updater_downloaded.load(std::sync::atomic::Ordering::SeqCst) }