diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4a76d36e..4707ffb5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -11,6 +11,18 @@ use tauri::Manager; use tauri_plugin_log::{RotationStrategy, Target, TargetKind, TimezoneStrategy}; use ws_broadcast::WsBroadcast; +#[cfg(target_os = "windows")] +fn clear_webview_boot_marker() { + let Some(exe_dir) = std::env::current_exe() + .ok() + .and_then(|path| path.parent().map(|dir| dir.to_path_buf())) + else { + return; + }; + let marker = exe_dir.join("cache").join("webview_boot_pending"); + let _ = std::fs::remove_file(marker); +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { // 日志目录:exe 目录/debug/logs(与前端日志同目录) @@ -56,6 +68,9 @@ pub fn run() { .build(), ) .setup(|app| { + #[cfg(target_os = "windows")] + clear_webview_boot_marker(); + // 创建 MaaState 并注册为 Tauri 管理状态 let maa_state = Arc::new(MaaState::default()); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index df379472..d87cc57e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -4,34 +4,128 @@ #[cfg(target_os = "windows")] mod webview2; +#[cfg(target_os = "windows")] +fn exe_dir() -> Option { + std::env::current_exe() + .ok() + .and_then(|path| path.parent().map(|dir| dir.to_path_buf())) +} + +#[cfg(target_os = "windows")] +fn bootstrap_log(message: &str) { + use std::io::Write; + + let Some(exe_dir) = exe_dir() else { + return; + }; + let debug_dir = exe_dir.join("debug"); + let _ = std::fs::create_dir_all(&debug_dir); + let log_path = debug_dir.join("bootstrap.log"); + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(log_path) + { + let _ = writeln!(file, "{}", message); + } +} + +#[cfg(target_os = "windows")] +fn webview_boot_marker_path() -> Option { + exe_dir().map(|dir| dir.join("cache").join("webview_boot_pending")) +} + +#[cfg(target_os = "windows")] +fn mark_webview_boot_pending() { + let Some(marker) = webview_boot_marker_path() else { + return; + }; + if let Some(parent) = marker.parent() { + let _ = std::fs::create_dir_all(parent); + } + let _ = std::fs::write(marker, b"pending"); +} + +#[cfg(target_os = "windows")] +fn repair_webview_data_after_previous_failure() { + let Some(marker) = webview_boot_marker_path() else { + return; + }; + if !marker.exists() { + return; + } + + bootstrap_log("previous WebView boot did not reach Tauri setup; rotating webview_data"); + let _ = std::fs::remove_file(&marker); + + let Some(exe_dir) = exe_dir() else { + return; + }; + let webview_data_dir = exe_dir.join("cache").join("webview_data"); + if !webview_data_dir.exists() { + return; + } + + let backup_name = format!( + "webview_data.bak-{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0) + ); + let backup_dir = exe_dir.join("cache").join(backup_name); + match std::fs::rename(&webview_data_dir, &backup_dir) { + Ok(()) => bootstrap_log("rotated webview_data successfully"), + Err(e) => bootstrap_log(&format!("failed to rotate webview_data: {}", e)), + } +} + +#[cfg(target_os = "windows")] +fn is_fixed_webview2_runtime_usable(runtime_dir: &std::path::Path) -> bool { + runtime_dir.is_dir() + && runtime_dir.join("msedgewebview2.exe").is_file() + && runtime_dir.join("EBWebView").is_dir() +} + fn main() { + #[cfg(target_os = "windows")] + bootstrap_log("mxu bootstrap start"); + if mxu_lib::commands::system::has_help_flag() { + #[cfg(target_os = "windows")] + bootstrap_log("help flag detected; exiting"); mxu_lib::commands::system::print_cli_help_text(); std::process::exit(0); } #[cfg(target_os = "windows")] { + repair_webview_data_after_previous_failure(); + // 设置 WebView2 数据目录为程序所在目录下的 webview_data 文件夹 // 这样可以避免用户名包含特殊字符(如中文)导致 WebView2 无法创建数据目录的问题 - if let Ok(exe_path) = std::env::current_exe() { - if let Some(exe_dir) = exe_path.parent() { - let webview_data_dir = exe_dir.join("cache").join("webview_data"); - // 确保目录存在 - let _ = std::fs::create_dir_all(&webview_data_dir); - std::env::set_var("WEBVIEW2_USER_DATA_FOLDER", &webview_data_dir); - - // 检测已缓存的 WebView2 固定版本运行时 - // 验证目录包含关键文件以确保运行时完整可用 - if let Ok(webview2_runtime_dir) = webview2::get_webview2_runtime_dir() { - if webview2_runtime_dir.is_dir() - && webview2_runtime_dir.join("msedgewebview2.exe").exists() - { - std::env::set_var( - "WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", - &webview2_runtime_dir, - ); - } + if let Some(exe_dir) = exe_dir() { + let webview_data_dir = exe_dir.join("cache").join("webview_data"); + // 确保目录存在 + match std::fs::create_dir_all(&webview_data_dir) { + Ok(()) => bootstrap_log("webview_data directory ready"), + Err(e) => bootstrap_log(&format!("failed to create webview_data: {}", e)), + } + std::env::set_var("WEBVIEW2_USER_DATA_FOLDER", &webview_data_dir); + + // 检测已缓存的 WebView2 固定版本运行时 + // 验证目录包含关键文件以确保运行时完整可用,否则回退到系统 WebView2/重新下载 + if let Ok(webview2_runtime_dir) = webview2::get_webview2_runtime_dir() { + if is_fixed_webview2_runtime_usable(&webview2_runtime_dir) { + std::env::set_var( + "WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", + &webview2_runtime_dir, + ); + bootstrap_log("using fixed WebView2 runtime"); + } else if webview2_runtime_dir.exists() { + bootstrap_log( + "fixed WebView2 runtime is incomplete; falling back to system/runtime install", + ); } } } @@ -40,6 +134,7 @@ fn main() { if std::env::var_os("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER").is_none() && !webview2::ensure_webview2() { + bootstrap_log("WebView2 is unavailable; exiting before Tauri run"); std::process::exit(1); } @@ -51,6 +146,8 @@ fn main() { Ok(p) => p, Err(_) => { // 获取路径失败就按普通权限继续 + bootstrap_log("failed to get current exe before elevation; entering run"); + mark_webview_boot_pending(); mxu_lib::run(); return; } @@ -69,10 +166,18 @@ fn main() { if result.is_ok() { // 新的管理员进程已启动,退出当前普通权限进程 + bootstrap_log("elevated process started; exiting non-elevated bootstrap"); std::process::exit(0); } + bootstrap_log("elevation failed or was cancelled; continuing normally"); } } + #[cfg(target_os = "windows")] + { + bootstrap_log("entering mxu_lib::run"); + mark_webview_boot_pending(); + } + mxu_lib::run() }