From d18b6bb898e3bcd2834d5db011cf4a0e3f0a897d Mon Sep 17 00:00:00 2001 From: BTMuli Date: Wed, 3 Dec 2025 20:30:28 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=8F=90=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands.rs | 87 --------------------------------------- src-tauri/src/lib.rs | 17 ++++---- src-tauri/src/watchdog.rs | 79 +++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 96 deletions(-) create mode 100644 src-tauri/src/watchdog.rs diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 1ba6a799..be6119a1 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -127,90 +127,3 @@ 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 { - 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(app_handle: AppHandle) -> Result<(), String> { - #[cfg(not(target_os = "windows"))] - { - return Err("This function is only supported on Windows.".into()); - } - - #[cfg(target_os = "windows")] - { - 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}"))?; - - // 立即退出:单例锁释放 - app_handle.exit(0); - Ok(()) - } -} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 7a09c08c..a7eb7f29 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,15 +6,13 @@ mod commands; mod plugins; mod utils; #[cfg(target_os = "windows")] +mod watchdog; +#[cfg(target_os = "windows")] mod yae; use crate::client::create_mhy_client; -use crate::commands::{ - create_window, execute_js, get_dir_size, init_app, is_in_admin, run_watchdog, run_with_admin, -}; +use crate::commands::{create_window, execute_js, get_dir_size, init_app, is_in_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, Manager, Window, WindowEvent}; // 窗口事件处理 @@ -56,7 +54,7 @@ pub fn run() { } } // 等父进程退出后再 runas 启动管理员实例,传入 --elevated 标志 - let _ = run_watchdog(ppid, "--elevated"); + let _ = watchdog::run_watchdog(ppid, "--elevated"); // 看门狗退出 return; } @@ -93,10 +91,11 @@ pub fn run() { execute_js, get_dir_size, create_mhy_client, - #[cfg(target_os = "windows")] - call_yae_dll, is_in_admin, - run_with_admin + #[cfg(target_os = "windows")] + yae::call_yae_dll, + #[cfg(target_os = "windows")] + watchdog::run_with_admin ]) .run(generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/watchdog.rs b/src-tauri/src/watchdog.rs new file mode 100644 index 00000000..36598854 --- /dev/null +++ b/src-tauri/src/watchdog.rs @@ -0,0 +1,79 @@ +//! 重启提权相关处理 +//! @since Beta v0.8.7 +#![cfg(target_os = "windows")] + +use std::ffi::OsStr; +use std::iter::once; +use std::os::windows::ffi::OsStrExt; +use std::ptr::null_mut; +use std::time::Duration; +use tauri::AppHandle; +use windows_sys::Win32::Foundation::{HANDLE, HWND}; +use windows_sys::Win32::Storage::FileSystem::SYNCHRONIZE; +use windows_sys::Win32::System::Threading::{OpenProcess, WaitForSingleObject, INFINITE}; +use windows_sys::Win32::UI::Shell::ShellExecuteW; +use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; + +// 带参数启动 +fn shell_runas_with_args(args: &str) -> Result<(), String> { + fn to_wide(s: &OsStr) -> Vec { + 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()) + } + } +} + +// 等待父进程退出(释放单例锁)后,再以管理员身份启动新实例 +pub fn run_watchdog(parent_pid: u32, args_to_pass: &str) -> Result<(), String> { + // 打开父进程句柄用于等待 + 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(app_handle: AppHandle) -> Result<(), String> { + 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}"))?; + + // 立即退出:单例锁释放 + app_handle.exit(0); + Ok(()) +}