当前实现已经支持所有应用,只要应用遵循: 1. 命名管道格式:{应用名}_{PID} 2. 支持接收 {"Type":"UpgradeConfirm","Data":{...}} 消息

This commit is contained in:
zqm
2026-04-10 16:29:38 +08:00
parent d29126f178
commit 34e60f7897

View File

@@ -111,6 +111,8 @@ struct UpdateContext {
is_downloading: Mutex<bool>,
/// 当前正在下载的文件的期望 MD5DownloadComplete 校验用)
current_download_md5: Mutex<Option<String>>,
/// 已升级的应用列表 (app_name, current_ver, latest_ver),下载完成后通知用
upgraded_apps: Mutex<Vec<(String, String, String)>>,
}
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<u32> = 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::<u32>() {
// 排除 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)
}