当前实现已经支持所有应用,只要应用遵循: 1. 命名管道格式:{应用名}_{PID} 2. 支持接收 {"Type":"UpgradeConfirm","Data":{...}} 消息
This commit is contained in:
@@ -111,6 +111,8 @@ struct UpdateContext {
|
||||
is_downloading: Mutex<bool>,
|
||||
/// 当前正在下载的文件的期望 MD5(DownloadComplete 校验用)
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user