mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-05-09 00:34:07 +08:00
🐛 重构管理员权限重启逻辑
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//! 命令模块,负责处理命令
|
||||
//! @since Beta v0.7.8
|
||||
//! @since Beta v0.8.8
|
||||
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
||||
use tauri_utils::config::{WebviewUrl, WindowConfig};
|
||||
@@ -128,57 +128,89 @@ pub fn is_in_admin() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn shell_runas_with_args(args: &str) -> Result<(), String> {
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::once;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::ptr::null_mut;
|
||||
|
||||
use windows_sys::Win32::Foundation::HWND;
|
||||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||||
|
||||
fn to_wide(s: &OsStr) -> Vec<u16> {
|
||||
s.encode_wide().chain(once(0)).collect()
|
||||
}
|
||||
|
||||
let exe_path = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||
let exe_wide = to_wide(exe_path.as_os_str());
|
||||
let args_wide = to_wide(OsStr::new(args));
|
||||
let cwd_wide =
|
||||
exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new("")));
|
||||
|
||||
unsafe {
|
||||
let result = ShellExecuteW(
|
||||
0 as HWND,
|
||||
to_wide(OsStr::new("runas")).as_ptr(),
|
||||
exe_wide.as_ptr(),
|
||||
args_wide.as_ptr(),
|
||||
if cwd_wide.len() > 1 { cwd_wide.as_ptr() } else { null_mut() },
|
||||
SW_SHOWNORMAL,
|
||||
);
|
||||
if (result as usize) > 32 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Failed to ShellExecuteW runas".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 等待父进程退出(释放单例锁)后,再以管理员身份启动新实例
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn run_watchdog(parent_pid: u32, args_to_pass: &str) -> Result<(), String> {
|
||||
use std::time::Duration;
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
use windows_sys::Win32::Storage::FileSystem::SYNCHRONIZE;
|
||||
use windows_sys::Win32::System::Threading::{OpenProcess, WaitForSingleObject, INFINITE};
|
||||
|
||||
// 打开父进程句柄用于等待
|
||||
let handle: HANDLE = unsafe { OpenProcess(SYNCHRONIZE, 0, parent_pid) };
|
||||
if handle == std::ptr::null_mut() {
|
||||
// 如果拿不到句柄,可能父进程已退出,稍作等待后继续
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
} else {
|
||||
unsafe {
|
||||
WaitForSingleObject(handle, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
// 父进程已退出 → 触发 UAC 提权启动新实例
|
||||
shell_runas_with_args(args_to_pass)
|
||||
}
|
||||
|
||||
// 以管理员权限重启应用
|
||||
#[tauri::command]
|
||||
pub fn run_with_admin() -> Result<(), String> {
|
||||
pub fn run_with_admin(app_handle: AppHandle) -> Result<(), String> {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
return Err("This function is only supported on Windows.".into());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::once;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::ptr::null_mut;
|
||||
use windows_sys::Win32::Foundation::HWND;
|
||||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||||
let parent_pid = std::process::id();
|
||||
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||
let mut cmd = std::process::Command::new(exe);
|
||||
cmd
|
||||
.arg("--watchdog")
|
||||
.arg(format!("--ppid={}", parent_pid))
|
||||
// 看门狗不加载单例插件(通过参数决定 main 的初始化)
|
||||
.spawn()
|
||||
.map_err(|e| format!("spawn watchdog failed: {e}"))?;
|
||||
|
||||
fn to_wide(s: &OsStr) -> Vec<u16> {
|
||||
s.encode_wide().chain(once(0)).collect()
|
||||
}
|
||||
|
||||
let exe_path = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||
if !exe_path.exists() {
|
||||
return Err(format!("executable not found: {}", exe_path.display()));
|
||||
}
|
||||
|
||||
let elevated_arg = "--elevated";
|
||||
// /C start "" "<full_path>" --elevated-action=post_install
|
||||
let params = format!("/C start \"\" \"{}\" {}", exe_path.display(), elevated_arg);
|
||||
|
||||
let cmd_w = to_wide(OsStr::new("cmd.exe"));
|
||||
let verb_w = to_wide(OsStr::new("runas"));
|
||||
let params_w = to_wide(OsStr::new(¶ms));
|
||||
let workdir_w =
|
||||
exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new("")));
|
||||
|
||||
unsafe {
|
||||
let result = ShellExecuteW(
|
||||
0 as HWND,
|
||||
verb_w.as_ptr(),
|
||||
cmd_w.as_ptr(),
|
||||
params_w.as_ptr(),
|
||||
if workdir_w.len() > 1 { workdir_w.as_ptr() } else { null_mut() },
|
||||
SW_SHOWNORMAL,
|
||||
);
|
||||
|
||||
if (result as usize) > 32 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Failed to restart as administrator via cmd.".into())
|
||||
}
|
||||
}
|
||||
// 立即退出:单例锁释放
|
||||
app_handle.exit(0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! 主模块,用于启动应用
|
||||
//! @since Beta v0.7.8
|
||||
//! @since Beta v0.8.8
|
||||
|
||||
mod client;
|
||||
mod commands;
|
||||
@@ -10,12 +10,12 @@ mod yae;
|
||||
|
||||
use crate::client::create_mhy_client;
|
||||
use crate::commands::{
|
||||
create_window, execute_js, get_dir_size, init_app, is_in_admin, run_with_admin,
|
||||
create_window, execute_js, get_dir_size, init_app, is_in_admin, run_watchdog, run_with_admin,
|
||||
};
|
||||
use crate::plugins::{build_log_plugin, build_si_plugin};
|
||||
#[cfg(target_os = "windows")]
|
||||
use crate::yae::call_yae_dll;
|
||||
use tauri::{generate_context, generate_handler, Builder, Manager, Window, WindowEvent};
|
||||
use tauri::{generate_context, generate_handler, Manager, Window, WindowEvent};
|
||||
|
||||
// 窗口事件处理
|
||||
fn window_event_handler(app: &Window, event: &WindowEvent) {
|
||||
@@ -40,9 +40,35 @@ fn window_event_handler(app: &Window, event: &WindowEvent) {
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
Builder::default()
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let is_watchdog = args.iter().any(|a| a == "--watchdog");
|
||||
// 看门狗模式:不初始化 Tauri,不加载单例,纯等待 + 提权启动
|
||||
if is_watchdog {
|
||||
// 解析父进程 PID
|
||||
let mut ppid: u32 = 0;
|
||||
for a in &args {
|
||||
if let Some(rest) = a.strip_prefix("--ppid=") {
|
||||
if let Ok(v) = rest.parse::<u32>() {
|
||||
ppid = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 等父进程退出后再 runas 启动管理员实例,传入 --elevated 标志
|
||||
let _ = run_watchdog(ppid, "--elevated");
|
||||
// 看门狗退出
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 正常应用实例:加载单例插件,防止多实例
|
||||
let mut builder = tauri::Builder::default();
|
||||
|
||||
// 只有在正常/管理员实例下才加载单例插件;看门狗不加载
|
||||
builder = builder.plugin(build_si_plugin());
|
||||
builder
|
||||
.on_window_event(move |app, event| window_event_handler(app, event))
|
||||
.plugin(build_si_plugin())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
|
||||
@@ -12,13 +12,13 @@ use tauri_plugin_single_instance::init;
|
||||
pub fn build_si_plugin<R: Runtime>() -> TauriPlugin<R> {
|
||||
init(move |app, argv, _cwd| {
|
||||
// 把 argv 转成 Vec<String>
|
||||
let args: Vec<String> = argv.iter().map(|s| s.to_string()).collect();
|
||||
// let args: Vec<String> = argv.iter().map(|s| s.to_string()).collect();
|
||||
|
||||
// 如果包含提升约定参数,发出专门事件并短路退出
|
||||
if args.iter().any(|a| a == "--elevated") {
|
||||
// 提升实例通常只负责传参或执行一次性任务,退出避免与主实例冲突
|
||||
std::process::exit(0);
|
||||
}
|
||||
// if args.iter().any(|a| a == "--elevated") {
|
||||
// 提升实例通常只负责传参或执行一次性任务,退出避免与主实例冲突
|
||||
// std::process::exit(0);
|
||||
// }
|
||||
|
||||
// 非提升启动:按原逻辑广播 deep link
|
||||
if let Err(e) = app.emit("active_deep_link", argv) {
|
||||
|
||||
Reference in New Issue
Block a user