194 lines
6.7 KiB
Rust
194 lines
6.7 KiB
Rust
use std::fs;
|
||
use std::process::Command;
|
||
use std::thread::sleep;
|
||
use std::time::Duration;
|
||
|
||
fn main() {
|
||
// 检查是否已有多个 BootLoader 进程在运行
|
||
if is_process_running("BootLoader.exe") {
|
||
println!("Multiple BootLoader instances detected, exiting...");
|
||
return;
|
||
}
|
||
|
||
// 获取 BootLoader 启动目录所在盘的 AppData 目录
|
||
let appdata_dir = get_appdata_dir();
|
||
println!("AppData directory: {:?}", appdata_dir);
|
||
|
||
// 构建 Updater 相关文件路径
|
||
let updater_exe = appdata_dir.join("Updater.exe");
|
||
let updater_new_exe = appdata_dir.join("Updater.new.exe");
|
||
println!("Updater.exe path: {:?}", updater_exe);
|
||
println!("Updater.new.exe path: {:?}", updater_new_exe);
|
||
|
||
// 检查 Updater.new.exe 是否存在
|
||
if !updater_new_exe.exists() {
|
||
println!("Updater.new.exe does not exist, exiting...");
|
||
return;
|
||
}
|
||
|
||
// 等待 Updater 进程退出
|
||
println!("Waiting for Updater process to exit...");
|
||
if !wait_for_process_exit("Updater.exe") {
|
||
// 超时,退出 BootLoader
|
||
println!("BootLoader exiting due to timeout...");
|
||
return;
|
||
}
|
||
println!("Updater process exited");
|
||
|
||
// 直接覆盖 Updater.exe(fs::rename 在 Windows 上会自动替换已存在的文件)
|
||
println!("Renaming Updater.new.exe to Updater.exe...");
|
||
match fs::rename(&updater_new_exe, &updater_exe) {
|
||
Ok(_) => println!("Rename successful"),
|
||
Err(e) => println!("Rename failed: {:?}", e),
|
||
}
|
||
|
||
// 读取 Updater 的 debug_mode 配置,决定启动方式
|
||
let updater_debug_mode = load_updater_debug_mode(&appdata_dir);
|
||
println!("Updater debug_mode: {}", updater_debug_mode);
|
||
|
||
// 启动 Updater.exe
|
||
println!("Starting Updater.exe...");
|
||
let started = spawn_updater(&updater_exe, updater_debug_mode);
|
||
|
||
if started {
|
||
println!("Updater.exe started successfully");
|
||
// 等待一点时间,确保 Updater 有足够的时间启动
|
||
sleep(Duration::from_secs(1));
|
||
// 检查 Updater 进程是否仍在运行
|
||
if is_process_running("Updater.exe") {
|
||
println!("Updater.exe is running");
|
||
} else {
|
||
println!("Updater.exe may have exited immediately");
|
||
}
|
||
} else {
|
||
println!("Failed to start Updater.exe");
|
||
}
|
||
}
|
||
|
||
/// 读取 Updater 的配置文件,获取 debug_mode
|
||
fn load_updater_debug_mode(appdata_dir: &std::path::Path) -> bool {
|
||
let config_path = appdata_dir.join("Updater").join("config.json");
|
||
if let Ok(content) = fs::read_to_string(&config_path) {
|
||
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
|
||
return json.get("debug_mode").and_then(|v| v.as_bool()).unwrap_or(false);
|
||
}
|
||
}
|
||
false
|
||
}
|
||
|
||
/// 根据 debug_mode 以不同方式启动 Updater.exe:
|
||
/// - debug_mode = true → CREATE_NEW_CONSOLE:Updater 获得独立的新终端窗口
|
||
/// - debug_mode = false → DETACHED_PROCESS:Updater 完全后台运行,无控制台
|
||
fn spawn_updater(updater_exe: &std::path::Path, debug_mode: bool) -> bool {
|
||
use std::os::windows::process::CommandExt;
|
||
|
||
// Windows 进程创建标志
|
||
const CREATE_NEW_CONSOLE: u32 = 0x00000010;
|
||
const DETACHED_PROCESS: u32 = 0x00000008;
|
||
|
||
let creation_flags = if debug_mode {
|
||
CREATE_NEW_CONSOLE // 新建独立控制台窗口
|
||
} else {
|
||
DETACHED_PROCESS // 完全脱离控制台,后台静默运行
|
||
};
|
||
|
||
match Command::new(updater_exe)
|
||
.creation_flags(creation_flags)
|
||
.spawn()
|
||
{
|
||
Ok(child) => {
|
||
drop(child);
|
||
true
|
||
}
|
||
Err(e) => {
|
||
println!("Failed to start Updater.exe: {:?}", e);
|
||
false
|
||
}
|
||
}
|
||
}
|
||
|
||
fn is_process_running(process_name: &str) -> bool {
|
||
use std::process::id;
|
||
|
||
let current_pid = id().to_string();
|
||
let output = Command::new("tasklist")
|
||
.args(["/FI", &format!("IMAGENAME eq {}", process_name), "/FO", "CSV"])
|
||
.output()
|
||
.expect("Failed to execute tasklist");
|
||
|
||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||
|
||
// 检查是否有其他同名进程
|
||
let lines: Vec<&str> = output_str.lines().collect();
|
||
let mut count = 0;
|
||
|
||
for line in lines {
|
||
if line.contains(&format!("\"{}\"", process_name)) && !line.contains(¤t_pid) {
|
||
count += 1;
|
||
}
|
||
}
|
||
|
||
// 如果有其他同名进程,则返回 true
|
||
count > 0
|
||
}
|
||
|
||
fn get_appdata_dir() -> std::path::PathBuf {
|
||
let exe_path = std::env::current_exe().expect("Failed to get executable path");
|
||
println!("BootLoader executable path: {:?}", exe_path);
|
||
let drive = if let Some(parent) = exe_path.parent() {
|
||
println!("Parent directory: {:?}", parent);
|
||
let parent_str = parent.as_os_str().to_str().unwrap_or("C:/");
|
||
println!("Parent directory string: {:?}", parent_str);
|
||
let drive = parent_str.split('\\').next().unwrap_or("C:");
|
||
println!("Drive: {:?}", drive);
|
||
std::path::Path::new(&format!("{}/", drive)).to_path_buf()
|
||
} else {
|
||
std::path::Path::new("C:/").to_path_buf()
|
||
};
|
||
let appdata_dir = drive.join("AppData");
|
||
println!("Final AppData directory: {:?}", appdata_dir);
|
||
appdata_dir
|
||
}
|
||
|
||
fn wait_for_process_exit(process_name: &str) -> bool {
|
||
let mut attempts = 0;
|
||
let max_attempts = 300; // 30 seconds
|
||
|
||
loop {
|
||
// 使用 tasklist 命令检查进程是否存在,使用 CSV 格式以便更精确地匹配
|
||
let output = Command::new("tasklist")
|
||
.args(["/FI", &format!("IMAGENAME eq {}", process_name), "/FO", "CSV"])
|
||
.output()
|
||
.expect("Failed to execute tasklist");
|
||
|
||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||
println!("Tasklist output for {}: {:?}", process_name, output_str);
|
||
|
||
// 检查是否有精确匹配的进程名
|
||
let mut process_found = false;
|
||
let lines: Vec<&str> = output_str.lines().collect();
|
||
|
||
for line in lines {
|
||
// 查找精确匹配的进程名
|
||
if line.contains(&format!("\"{}\"", process_name)) {
|
||
process_found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if !process_found {
|
||
println!("Process {} not found, continuing...", process_name);
|
||
return true; // 进程成功退出
|
||
}
|
||
|
||
attempts += 1;
|
||
if attempts > max_attempts {
|
||
println!("Timeout waiting for process {} to exit, exiting BootLoader...", process_name);
|
||
return false; // 超时,返回 false
|
||
}
|
||
|
||
// 等待 100 毫秒后再次检查
|
||
sleep(Duration::from_millis(100));
|
||
}
|
||
}
|