升级完成重启应用程序
This commit is contained in:
@@ -105,8 +105,8 @@ struct UpdateContext {
|
|||||||
server_updater_version: Mutex<Option<String>>,
|
server_updater_version: Mutex<Option<String>>,
|
||||||
/// 当前更新阶段
|
/// 当前更新阶段
|
||||||
current_phase: Mutex<UpdatePhase>,
|
current_phase: Mutex<UpdatePhase>,
|
||||||
/// 候选应用列表 (app_name, local_version),阶段3使用
|
/// 候选应用列表 (app_name, local_version, exe_path),阶段3使用
|
||||||
candidates: Mutex<Vec<(String, String)>>,
|
candidates: Mutex<Vec<(String, String, String)>>,
|
||||||
/// 等待 GetAllFile 响应的应用列表(阶段3.5使用)
|
/// 等待 GetAllFile 响应的应用列表(阶段3.5使用)
|
||||||
pending_allfile_apps: Mutex<Vec<String>>,
|
pending_allfile_apps: Mutex<Vec<String>>,
|
||||||
/// 待下载队列 (app_name, filename, offset, expected_md5),顺序下载
|
/// 待下载队列 (app_name, filename, offset, expected_md5),顺序下载
|
||||||
@@ -115,8 +115,8 @@ struct UpdateContext {
|
|||||||
is_downloading: Mutex<bool>,
|
is_downloading: Mutex<bool>,
|
||||||
/// 当前正在下载的文件的期望 MD5(DownloadComplete 校验用)
|
/// 当前正在下载的文件的期望 MD5(DownloadComplete 校验用)
|
||||||
current_download_md5: Mutex<Option<String>>,
|
current_download_md5: Mutex<Option<String>>,
|
||||||
/// 已升级的应用列表 (app_name, current_ver, latest_ver),下载完成后通知用
|
/// 已升级的应用列表 (app_name, current_ver, latest_ver, exe_path),下载完成后通知用
|
||||||
upgraded_apps: Mutex<Vec<(String, String, String)>>,
|
upgraded_apps: Mutex<Vec<(String, String, String, String)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UpdateContext {
|
impl Default for UpdateContext {
|
||||||
@@ -127,7 +127,7 @@ impl Default for UpdateContext {
|
|||||||
app_completed_set: Mutex::new(HashSet::new()),
|
app_completed_set: Mutex::new(HashSet::new()),
|
||||||
server_updater_version: Mutex::new(None),
|
server_updater_version: Mutex::new(None),
|
||||||
current_phase: Mutex::new(UpdatePhase::BootLoader),
|
current_phase: Mutex::new(UpdatePhase::BootLoader),
|
||||||
candidates: Mutex::new(Vec::new()),
|
candidates: Mutex::new(Vec::<(String, String, String)>::new()),
|
||||||
pending_allfile_apps: Mutex::new(Vec::new()),
|
pending_allfile_apps: Mutex::new(Vec::new()),
|
||||||
download_queue: Mutex::new(std::collections::VecDeque::new()),
|
download_queue: Mutex::new(std::collections::VecDeque::new()),
|
||||||
is_downloading: Mutex::new(false),
|
is_downloading: Mutex::new(false),
|
||||||
@@ -1185,41 +1185,10 @@ fn is_process_running_ex(process_name: &str, exclude_pid: Option<u32>) -> bool {
|
|||||||
count > 0
|
count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 从运行中进程获取版本号(通过进程的可执行文件路径)
|
/// 获取 AppData 目录下所有候选应用(排除 Updater)
|
||||||
#[cfg(windows)]
|
/// 返回 (app_name, local_version, exe_path) 列表
|
||||||
fn get_version_from_process(app_name: &str) -> String {
|
/// 版本号和路径在进程运行期间直接从进程路径读取,保证与进程实际加载的一致
|
||||||
let ps_script = format!(
|
fn get_app_candidates(debug: bool) -> Vec<(String, String, String)> {
|
||||||
"$p = Get-Process -Name '{}' -ErrorAction SilentlyContinue | Select-Object -First 1; \
|
|
||||||
if ($p -and $p.Path) {{ (Get-Item $p.Path -ErrorAction SilentlyContinue).VersionInfo.FileVersion }} \
|
|
||||||
elseif ($p -and $p.Id) {{ \
|
|
||||||
$wmi = Get-CimInstance Win32_Process -Filter \"ProcessId=$($p.Id)\" -ErrorAction SilentlyContinue; \
|
|
||||||
if ($wmi.ExecutablePath) {{ (Get-Item $wmi.ExecutablePath -ErrorAction SilentlyContinue).VersionInfo.FileVersion }} \
|
|
||||||
}}",
|
|
||||||
app_name.replace("'", "''")
|
|
||||||
);
|
|
||||||
|
|
||||||
let output = Command::new("powershell")
|
|
||||||
.args(["-NoProfile", "-NonInteractive", "-Command", &ps_script])
|
|
||||||
.output();
|
|
||||||
|
|
||||||
if let Ok(output) = output {
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
||||||
let version = stdout.trim();
|
|
||||||
if !version.is_empty() && version != "0" && !version.contains("没有文件") {
|
|
||||||
return version.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"0.0.0".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn get_version_from_process(_app_name: &str) -> String {
|
|
||||||
"0.0.0".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取 AppData 目录下所有候选应用(排除 Updater,返回 (app_name, local_version) 列表)
|
|
||||||
/// 版本号在进程运行期间直接从进程路径读取,保证版本与进程实际加载的一致
|
|
||||||
fn get_app_candidates(debug: bool) -> Vec<(String, String)> {
|
|
||||||
let appdata = get_updater_data_dir(); // X:\AppData\
|
let appdata = get_updater_data_dir(); // X:\AppData\
|
||||||
if !appdata.exists() {
|
if !appdata.exists() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
@@ -1233,14 +1202,13 @@ fn get_app_candidates(debug: bool) -> Vec<(String, String)> {
|
|||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||||
if name != "Updater" && !name.starts_with('.') {
|
if name != "Updater" && !name.starts_with('.') {
|
||||||
// 检查该目录是否对应一个运行中的进程
|
|
||||||
let exe_name = format!("{}.exe", name);
|
let exe_name = format!("{}.exe", name);
|
||||||
if is_process_running(&exe_name) {
|
if is_process_running(&exe_name) {
|
||||||
let local_version = get_version_from_process(name);
|
let (local_version, exe_path) = get_version_and_path_from_process(name);
|
||||||
if debug {
|
if debug {
|
||||||
println!("[应用] 候选应用: {} v{} (进程运行中)", name, local_version);
|
println!("[应用] 候选应用: {} v{} ({})", name, local_version, exe_path);
|
||||||
}
|
}
|
||||||
candidates.push((name.to_string(), local_version));
|
candidates.push((name.to_string(), local_version, exe_path));
|
||||||
} else if debug {
|
} else if debug {
|
||||||
println!("[应用] 跳过 {} (进程未运行)", name);
|
println!("[应用] 跳过 {} (进程未运行)", name);
|
||||||
}
|
}
|
||||||
@@ -1253,6 +1221,151 @@ fn get_app_candidates(debug: bool) -> Vec<(String, String)> {
|
|||||||
candidates
|
candidates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 从运行中进程获取版本号和可执行文件路径
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn get_version_and_path_from_process(app_name: &str) -> (String, String) {
|
||||||
|
let ps_script = format!(
|
||||||
|
"$p = Get-Process -Name '{}' -ErrorAction SilentlyContinue | Select-Object -First 1; \
|
||||||
|
if ($p -and $p.Path) {{ \
|
||||||
|
$v = (Get-Item $p.Path -ErrorAction SilentlyContinue).VersionInfo.FileVersion; \
|
||||||
|
\"$v|$($p.Path)\" \
|
||||||
|
}} elseif ($p -and $p.Id) {{ \
|
||||||
|
$wmi = Get-CimInstance Win32_Process -Filter \"ProcessId=$($p.Id)\" -ErrorAction SilentlyContinue; \
|
||||||
|
if ($wmi.ExecutablePath) {{ \
|
||||||
|
$v = (Get-Item $wmi.ExecutablePath -ErrorAction SilentlyContinue).VersionInfo.FileVersion; \
|
||||||
|
\"$v|$($wmi.ExecutablePath)\" \
|
||||||
|
}} \
|
||||||
|
}}",
|
||||||
|
app_name.replace("'", "''")
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = Command::new("powershell")
|
||||||
|
.args(["-NoProfile", "-NonInteractive", "-Command", &ps_script])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
if let Ok(output) = output {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let line = stdout.trim();
|
||||||
|
if !line.is_empty() && !line.contains("没有文件") {
|
||||||
|
if let Some(sep) = line.find('|') {
|
||||||
|
let version = line[..sep].trim().to_string();
|
||||||
|
let path = line[sep + 1..].trim().to_string();
|
||||||
|
if !version.is_empty() && version != "0" {
|
||||||
|
return (version, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("0.0.0".to_string(), String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行应用文件升级:把升级目录中的文件复制到目标目录
|
||||||
|
/// 升级目录:X:\AppData\Updater\UpGrade\{app_name}\*
|
||||||
|
/// 目标目录:exe_path 的父目录(如 C:\AppData\EasyTest\)
|
||||||
|
fn upgrade_app_files(app_name: &str, exe_path: &str) {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
||||||
|
|
||||||
|
let upgrade_base = get_updater_data_dir()
|
||||||
|
.join("Updater")
|
||||||
|
.join("UpGrade")
|
||||||
|
.join(app_name);
|
||||||
|
|
||||||
|
if !upgrade_base.exists() {
|
||||||
|
log_print!("{} [升级替换] {} 升级目录不存在: {:?}", ts, app_name, upgrade_base);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 目标目录 = exe_path 的父目录
|
||||||
|
let target_dir = std::path::Path::new(exe_path)
|
||||||
|
.parent()
|
||||||
|
.map(|p| p.to_path_buf())
|
||||||
|
.unwrap_or_else(|| std::path::PathBuf::from("."));
|
||||||
|
|
||||||
|
log_print!("{} [升级替换] {} -> {:?}", ts, app_name, target_dir);
|
||||||
|
|
||||||
|
let mut success_count = 0;
|
||||||
|
let mut fail_count = 0;
|
||||||
|
|
||||||
|
// 递归扫描升级目录
|
||||||
|
fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path, ts: &str) -> (i32, i32) {
|
||||||
|
let mut ok = 0;
|
||||||
|
let mut fail = 0;
|
||||||
|
|
||||||
|
let _ = fs::create_dir_all(dst);
|
||||||
|
|
||||||
|
if let Ok(entries) = fs::read_dir(src) {
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let src_path = entry.path();
|
||||||
|
let file_name = entry.file_name();
|
||||||
|
let dst_path = dst.join(&file_name);
|
||||||
|
|
||||||
|
if src_path.is_dir() {
|
||||||
|
let (o, f) = copy_dir_recursive(&src_path, &dst_path, ts);
|
||||||
|
ok += o;
|
||||||
|
fail += f;
|
||||||
|
} else {
|
||||||
|
// 跳过 .tmp 文件
|
||||||
|
if let Some(name) = file_name.to_str() {
|
||||||
|
if name.ends_with(".tmp") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先删除目标文件(可能只读)
|
||||||
|
let _ = fs::remove_file(&dst_path);
|
||||||
|
|
||||||
|
match fs::copy(&src_path, &dst_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
log_print!("{} [升级替换] {} -> {}",
|
||||||
|
ts, file_name.to_string_lossy(), dst_path.display());
|
||||||
|
ok += 1;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log_print!("{} [升级替换] 复制失败 {}: {}", ts, file_name.to_string_lossy(), e);
|
||||||
|
fail += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(ok, fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ok, fail) = copy_dir_recursive(&upgrade_base, &target_dir, ts.as_str());
|
||||||
|
success_count += ok;
|
||||||
|
fail_count += fail;
|
||||||
|
|
||||||
|
log_print!("{} [升级替换] {} 升级完成:成功 {} 个,失败 {} 个",
|
||||||
|
ts, app_name, success_count, fail_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重启指定应用(工作目录 = exe 所在目录)
|
||||||
|
fn restart_app(app_name: &str, exe_path: &str) {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
|
||||||
|
// 工作目录 = exe 所在目录
|
||||||
|
let work_dir = std::path::Path::new(exe_path)
|
||||||
|
.parent()
|
||||||
|
.map(|p| p.to_path_buf())
|
||||||
|
.unwrap_or_else(|| std::path::PathBuf::from("."));
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
match Command::new(exe_path)
|
||||||
|
.current_dir(&work_dir)
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Ok(child) => {
|
||||||
|
log_print!("{} [重启] 已启动 {} (PID={}),工作目录: {}",
|
||||||
|
ts, app_name, child.id(), work_dir.display());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log_print!("{} [重启] 启动失败 {}: {}", ts, exe_path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 递归扫描升级目录,返回 (relative_path, full_path) 列表
|
/// 递归扫描升级目录,返回 (relative_path, full_path) 列表
|
||||||
/// 跳过 .tmp 文件(如果存在同名非 tmp 文件的话)
|
/// 跳过 .tmp 文件(如果存在同名非 tmp 文件的话)
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -1385,7 +1498,8 @@ fn send_get_all_file(
|
|||||||
|
|
||||||
/// 向指定应用发送升级确认消息(修复版:直接用 Win32 API 连接管道,不再用 PowerShell)
|
/// 向指定应用发送升级确认消息(修复版:直接用 Win32 API 连接管道,不再用 PowerShell)
|
||||||
/// 消息格式:{"Type":"UpgradeConfirm","Data":{"AppName":"xxx","CurrentVer":"1.0.0","LatestVer":"1.1.0"}}
|
/// 消息格式:{"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) {
|
/// 返回 true = 用户批准,false = 用户拒绝/通信失败
|
||||||
|
fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, _debug: bool) -> bool {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -1404,13 +1518,13 @@ fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, _debu
|
|||||||
let s = String::from_utf8_lossy(&o.stdout);
|
let s = String::from_utf8_lossy(&o.stdout);
|
||||||
s.lines().filter_map(|l| l.trim().parse().ok()).collect()
|
s.lines().filter_map(|l| l.trim().parse().ok()).collect()
|
||||||
}
|
}
|
||||||
Err(_) => return,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if pids.is_empty() {
|
if pids.is_empty() {
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
log_print!("{} [升级确认] {} 未运行,跳过通知", ts, app_name);
|
log_print!("{} [升级确认] {} 未运行,跳过通知", ts, app_name);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 构造消息
|
// 2. 构造消息
|
||||||
@@ -1432,43 +1546,29 @@ fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, _debu
|
|||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
log_print!("{} [升级确认] 用户批准升级 {} (PID={}),应用将退出",
|
log_print!("{} [升级确认] 用户批准升级 {} (PID={}),应用将退出",
|
||||||
ts, app_name, pid);
|
ts, app_name, pid);
|
||||||
// 等待应用退出(最多 5 秒)
|
return true;
|
||||||
log_print!("{} [升级确认] 等待 {} 退出...", ts, app_name);
|
|
||||||
let start = std::time::Instant::now();
|
|
||||||
loop {
|
|
||||||
let check = Command::new("powershell")
|
|
||||||
.args(["-NoProfile", "-NonInteractive", "-Command",
|
|
||||||
&format!("if ((Get-Process -Id {pid} -ErrorAction SilentlyContinue) -eq $null) {{ 'exit' }} else {{ 'running' }}")])
|
|
||||||
.output();
|
|
||||||
if let Ok(out) = check {
|
|
||||||
if String::from_utf8_lossy(&out.stdout).contains("exit") {
|
|
||||||
log_print!("{} [升级确认] {} 已退出,升级完成", ts, app_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if start.elapsed().as_secs() > 5 {
|
|
||||||
log_print!("{} [升级确认] 等待退出超时,跳过", ts);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
log_print!("{} [升级确认] 用户拒绝升级 {} (PID={})",
|
log_print!("{} [升级确认] 用户拒绝升级 {} (PID={})",
|
||||||
ts, app_name, pid);
|
ts, app_name, pid);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log_print!("{} [升级确认] 通信失败 {} (PID={}): {}",
|
log_print!("{} [升级确认] 通信失败 {} (PID={}): {}",
|
||||||
ts, app_name, pid, e);
|
ts, app_name, pid, e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
{
|
{
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
log_print!("{} [升级确认] 非 Windows 平台,跳过通知", ts);
|
log_print!("{} [升级确认] 非 Windows 平台,跳过通知", ts);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1609,8 +1709,8 @@ fn connect_and_wait_response(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 通知所有升级了的应用(EasyTest 等)
|
/// 通知所有升级了的应用(EasyTest 等)
|
||||||
/// app_upgrades: Vec<(app_name, current_ver, latest_ver)>
|
/// app_upgrades: Vec<(app_name, current_ver, latest_ver, exe_path)>
|
||||||
fn notify_all_app_upgrades(app_upgrades: &[(String, String, String)], _debug: bool) {
|
fn notify_all_app_upgrades(app_upgrades: &[(String, String, String, String)], _debug: bool) {
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
|
||||||
if app_upgrades.is_empty() {
|
if app_upgrades.is_empty() {
|
||||||
@@ -1620,8 +1720,44 @@ fn notify_all_app_upgrades(app_upgrades: &[(String, String, String)], _debug: bo
|
|||||||
|
|
||||||
log_print!("{} [升级确认] 准备通知 {} 个应用: {:?}", ts, app_upgrades.len(), app_upgrades);
|
log_print!("{} [升级确认] 准备通知 {} 个应用: {:?}", ts, app_upgrades.len(), app_upgrades);
|
||||||
|
|
||||||
for (app_name, current_ver, latest_ver) in app_upgrades {
|
for (app_name, current_ver, latest_ver, exe_path) in app_upgrades {
|
||||||
notify_app_upgrade(app_name, current_ver, latest_ver, true); // 始终打印通知详情
|
// 发送升级确认消息,等待用户响应
|
||||||
|
let approved = notify_app_upgrade(app_name, current_ver, latest_ver, true);
|
||||||
|
|
||||||
|
// 用户批准升级后,等待应用退出,然后复制文件并重启
|
||||||
|
if approved {
|
||||||
|
// 等待应用退出(最多 15 秒)
|
||||||
|
log_print!("{} [升级确认] 等待 {} 退出...", ts, app_name);
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let mut exited = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let check = Command::new("powershell")
|
||||||
|
.args(["-NoProfile", "-NonInteractive", "-Command",
|
||||||
|
&format!("if ((Get-Process -Name '{}' -ErrorAction SilentlyContinue) -eq $null) {{ 'exit' }} else {{ 'running' }}", app_name)])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
if let Ok(out) = check {
|
||||||
|
if String::from_utf8_lossy(&out.stdout).contains("exit") {
|
||||||
|
exited = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if start.elapsed().as_secs() > 15 {
|
||||||
|
log_print!("{} [升级确认] 等待退出超时,跳过", ts);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用退出后,复制文件并重启
|
||||||
|
if exited {
|
||||||
|
upgrade_app_files(app_name, exe_path);
|
||||||
|
restart_app(app_name, exe_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1849,12 +1985,12 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps;
|
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps;
|
||||||
// 打印候选应用及其预存版本
|
// 打印候选应用及其预存版本
|
||||||
let app_names: Vec<String> = candidates.iter().map(|(n, v)| format!("{}(v{})", n, v)).collect();
|
let app_names: Vec<String> = candidates.iter().map(|(n, v, _)| format!("{}(v{})", n, v)).collect();
|
||||||
log_print!("{} [阶段3] 检查应用: {:?}", ts, app_names);
|
log_print!("{} [阶段3] 检查应用: {:?}", ts, app_names);
|
||||||
|
|
||||||
// 构建应用版本查询
|
// 构建应用版本查询
|
||||||
let mut file_list = Vec::new();
|
let mut file_list = Vec::new();
|
||||||
for (app_name, _) in &candidates {
|
for (app_name, _, _) in &candidates {
|
||||||
file_list.push(format!("{}\\{}.exe", app_name, app_name));
|
file_list.push(format!("{}\\{}.exe", app_name, app_name));
|
||||||
}
|
}
|
||||||
let file_list_json = serde_json::to_string(&file_list).unwrap_or_else(|_| "[]".to_string());
|
let file_list_json = serde_json::to_string(&file_list).unwrap_or_else(|_| "[]".to_string());
|
||||||
@@ -1879,11 +2015,11 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps;
|
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps;
|
||||||
// 打印候选应用及其预存版本
|
// 打印候选应用及其预存版本
|
||||||
let app_names: Vec<String> = candidates.iter().map(|(n, v)| format!("{}(v{})", n, v)).collect();
|
let app_names: Vec<String> = candidates.iter().map(|(n, v, _)| format!("{}(v{})", n, v)).collect();
|
||||||
println!("{} [阶段3] 检查应用: {:?}", ts, app_names);
|
println!("{} [阶段3] 检查应用: {:?}", ts, app_names);
|
||||||
|
|
||||||
let mut file_list = Vec::new();
|
let mut file_list = Vec::new();
|
||||||
for (app_name, _) in &candidates {
|
for (app_name, _, _) in &candidates {
|
||||||
file_list.push(format!("{}\\{}.exe", app_name, app_name));
|
file_list.push(format!("{}\\{}.exe", app_name, app_name));
|
||||||
}
|
}
|
||||||
let file_list_json = serde_json::to_string(&file_list).unwrap_or_else(|_| "[]".to_string());
|
let file_list_json = serde_json::to_string(&file_list).unwrap_or_else(|_| "[]".to_string());
|
||||||
@@ -1902,9 +2038,9 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
// 使用预存的版本(在候选发现阶段已从运行中进程读取)
|
// 使用预存的版本(在候选发现阶段已从运行中进程读取)
|
||||||
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
||||||
let mut apps_to_update = Vec::new();
|
let mut apps_to_update = Vec::new();
|
||||||
let mut upgraded_apps = Vec::new(); // (app_name, current_ver, latest_ver)
|
let mut upgraded_apps = Vec::new(); // (app_name, current_ver, latest_ver, exe_path)
|
||||||
|
|
||||||
for (app_name, local_version) in candidates {
|
for (app_name, local_version, exe_path) in candidates {
|
||||||
let exe_name = format!("{}\\{}.exe", app_name, app_name);
|
let exe_name = format!("{}\\{}.exe", app_name, app_name);
|
||||||
if let Some(server_ver) = file_versions.get(&exe_name) {
|
if let Some(server_ver) = file_versions.get(&exe_name) {
|
||||||
let server_version = server_ver.as_str().unwrap_or("");
|
let server_version = server_ver.as_str().unwrap_or("");
|
||||||
@@ -1919,7 +2055,7 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
if local_version == "0.0.0" || version_less_than(&local_version, server_version) {
|
if local_version == "0.0.0" || version_less_than(&local_version, server_version) {
|
||||||
apps_to_update.push(app_name.clone());
|
apps_to_update.push(app_name.clone());
|
||||||
// 记录需要升级的应用信息(用于后续发送通知)
|
// 记录需要升级的应用信息(用于后续发送通知)
|
||||||
upgraded_apps.push((app_name.clone(), local_version.clone(), server_version.to_string()));
|
upgraded_apps.push((app_name.clone(), local_version.clone(), server_version.to_string(), exe_path.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2221,7 +2357,7 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
log_print!("{} [FileChunk] is_last={}, queue_len={}", ts, is_last, queue_len);
|
log_print!("{} [FileChunk] is_last={}, queue_len={}", ts, is_last, queue_len);
|
||||||
|
|
||||||
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
||||||
for (app_name, _) in &candidates {
|
for (app_name, _, _) in &candidates {
|
||||||
let result = handle_app_file_chunk(&ctx_clone, app_name, data_obj, debug_msg);
|
let result = handle_app_file_chunk(&ctx_clone, app_name, data_obj, debug_msg);
|
||||||
if let Some((_, relative_path)) = result {
|
if let Some((_, relative_path)) = result {
|
||||||
if is_last {
|
if is_last {
|
||||||
@@ -2287,9 +2423,9 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
// ========== 阶段3.5:处理应用 DownloadComplete ==========
|
// ========== 阶段3.5:处理应用 DownloadComplete ==========
|
||||||
if current_phase == UpdatePhase::AppsWaitAllFile {
|
if current_phase == UpdatePhase::AppsWaitAllFile {
|
||||||
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
||||||
let candidate_names: Vec<&str> = candidates.iter().map(|(n, _)| n.as_str()).collect();
|
let candidate_names: Vec<&str> = candidates.iter().map(|(n, _, _)| n.as_str()).collect();
|
||||||
log_print!("{} [DownloadComplete] 文件: {}, 候选应用: {:?}", ts, filename, candidate_names);
|
log_print!("{} [DownloadComplete] 文件: {}, 候选应用: {:?}", ts, filename, candidate_names);
|
||||||
for (app_name, _) in &candidates {
|
for (app_name, _, _) in &candidates {
|
||||||
// filename 可能含路径分隔符(如 "EasyTest/Audio.wav"),只取纯文件名部分
|
// filename 可能含路径分隔符(如 "EasyTest/Audio.wav"),只取纯文件名部分
|
||||||
let tmp_filename = std::path::Path::new(filename)
|
let tmp_filename = std::path::Path::new(filename)
|
||||||
.file_name()
|
.file_name()
|
||||||
|
|||||||
Reference in New Issue
Block a user