|
1 | 1 | //! 命令模块,负责处理命令 |
2 | | -//! @since Beta v0.7.8 |
| 2 | +//! @since Beta v0.8.8 |
3 | 3 |
|
4 | 4 | use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder}; |
5 | 5 | use tauri_utils::config::{WebviewUrl, WindowConfig}; |
@@ -128,57 +128,89 @@ pub fn is_in_admin() -> bool { |
128 | 128 | } |
129 | 129 | } |
130 | 130 |
|
| 131 | +#[cfg(target_os = "windows")] |
| 132 | +pub fn shell_runas_with_args(args: &str) -> Result<(), String> { |
| 133 | + use std::ffi::OsStr; |
| 134 | + use std::iter::once; |
| 135 | + use std::os::windows::ffi::OsStrExt; |
| 136 | + use std::ptr::null_mut; |
| 137 | + |
| 138 | + use windows_sys::Win32::Foundation::HWND; |
| 139 | + use windows_sys::Win32::UI::Shell::ShellExecuteW; |
| 140 | + use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; |
| 141 | + |
| 142 | + fn to_wide(s: &OsStr) -> Vec<u16> { |
| 143 | + s.encode_wide().chain(once(0)).collect() |
| 144 | + } |
| 145 | + |
| 146 | + let exe_path = std::env::current_exe().map_err(|e| e.to_string())?; |
| 147 | + let exe_wide = to_wide(exe_path.as_os_str()); |
| 148 | + let args_wide = to_wide(OsStr::new(args)); |
| 149 | + let cwd_wide = |
| 150 | + exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new(""))); |
| 151 | + |
| 152 | + unsafe { |
| 153 | + let result = ShellExecuteW( |
| 154 | + 0 as HWND, |
| 155 | + to_wide(OsStr::new("runas")).as_ptr(), |
| 156 | + exe_wide.as_ptr(), |
| 157 | + args_wide.as_ptr(), |
| 158 | + if cwd_wide.len() > 1 { cwd_wide.as_ptr() } else { null_mut() }, |
| 159 | + SW_SHOWNORMAL, |
| 160 | + ); |
| 161 | + if (result as usize) > 32 { |
| 162 | + Ok(()) |
| 163 | + } else { |
| 164 | + Err("Failed to ShellExecuteW runas".into()) |
| 165 | + } |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +// 等待父进程退出(释放单例锁)后,再以管理员身份启动新实例 |
| 170 | +#[cfg(target_os = "windows")] |
| 171 | +pub fn run_watchdog(parent_pid: u32, args_to_pass: &str) -> Result<(), String> { |
| 172 | + use std::time::Duration; |
| 173 | + use windows_sys::Win32::Foundation::HANDLE; |
| 174 | + use windows_sys::Win32::Storage::FileSystem::SYNCHRONIZE; |
| 175 | + use windows_sys::Win32::System::Threading::{OpenProcess, WaitForSingleObject, INFINITE}; |
| 176 | + |
| 177 | + // 打开父进程句柄用于等待 |
| 178 | + let handle: HANDLE = unsafe { OpenProcess(SYNCHRONIZE, 0, parent_pid) }; |
| 179 | + if handle == std::ptr::null_mut() { |
| 180 | + // 如果拿不到句柄,可能父进程已退出,稍作等待后继续 |
| 181 | + std::thread::sleep(Duration::from_millis(300)); |
| 182 | + } else { |
| 183 | + unsafe { |
| 184 | + WaitForSingleObject(handle, INFINITE); |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + // 父进程已退出 → 触发 UAC 提权启动新实例 |
| 189 | + shell_runas_with_args(args_to_pass) |
| 190 | +} |
| 191 | + |
131 | 192 | // 以管理员权限重启应用 |
132 | 193 | #[tauri::command] |
133 | | -pub fn run_with_admin() -> Result<(), String> { |
| 194 | +pub fn run_with_admin(app_handle: AppHandle) -> Result<(), String> { |
134 | 195 | #[cfg(not(target_os = "windows"))] |
135 | 196 | { |
136 | 197 | return Err("This function is only supported on Windows.".into()); |
137 | 198 | } |
| 199 | + |
138 | 200 | #[cfg(target_os = "windows")] |
139 | 201 | { |
140 | | - use std::ffi::OsStr; |
141 | | - use std::iter::once; |
142 | | - use std::os::windows::ffi::OsStrExt; |
143 | | - use std::ptr::null_mut; |
144 | | - use windows_sys::Win32::Foundation::HWND; |
145 | | - use windows_sys::Win32::UI::Shell::ShellExecuteW; |
146 | | - use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; |
147 | | - |
148 | | - fn to_wide(s: &OsStr) -> Vec<u16> { |
149 | | - s.encode_wide().chain(once(0)).collect() |
150 | | - } |
151 | | - |
152 | | - let exe_path = std::env::current_exe().map_err(|e| e.to_string())?; |
153 | | - if !exe_path.exists() { |
154 | | - return Err(format!("executable not found: {}", exe_path.display())); |
155 | | - } |
156 | | - |
157 | | - let elevated_arg = "--elevated"; |
158 | | - // /C start "" "<full_path>" --elevated-action=post_install |
159 | | - let params = format!("/C start \"\" \"{}\" {}", exe_path.display(), elevated_arg); |
160 | | - |
161 | | - let cmd_w = to_wide(OsStr::new("cmd.exe")); |
162 | | - let verb_w = to_wide(OsStr::new("runas")); |
163 | | - let params_w = to_wide(OsStr::new(¶ms)); |
164 | | - let workdir_w = |
165 | | - exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new(""))); |
166 | | - |
167 | | - unsafe { |
168 | | - let result = ShellExecuteW( |
169 | | - 0 as HWND, |
170 | | - verb_w.as_ptr(), |
171 | | - cmd_w.as_ptr(), |
172 | | - params_w.as_ptr(), |
173 | | - if workdir_w.len() > 1 { workdir_w.as_ptr() } else { null_mut() }, |
174 | | - SW_SHOWNORMAL, |
175 | | - ); |
176 | | - |
177 | | - if (result as usize) > 32 { |
178 | | - Ok(()) |
179 | | - } else { |
180 | | - Err("Failed to restart as administrator via cmd.".into()) |
181 | | - } |
182 | | - } |
| 202 | + let parent_pid = std::process::id(); |
| 203 | + let exe = std::env::current_exe().map_err(|e| e.to_string())?; |
| 204 | + let mut cmd = std::process::Command::new(exe); |
| 205 | + cmd |
| 206 | + .arg("--watchdog") |
| 207 | + .arg(format!("--ppid={}", parent_pid)) |
| 208 | + // 看门狗不加载单例插件(通过参数决定 main 的初始化) |
| 209 | + .spawn() |
| 210 | + .map_err(|e| format!("spawn watchdog failed: {e}"))?; |
| 211 | + |
| 212 | + // 立即退出:单例锁释放 |
| 213 | + app_handle.exit(0); |
| 214 | + Ok(()) |
183 | 215 | } |
184 | 216 | } |
0 commit comments