|
3 | 3 | use std::fs; |
4 | 4 | use std::path::PathBuf; |
5 | 5 |
|
6 | | -#[cfg(windows)] |
7 | | -use windows::core::{PCWSTR, w}; |
8 | | -#[cfg(windows)] |
9 | | -use windows::Win32::Foundation::{CloseHandle, HANDLE, HWND}; |
10 | | -#[cfg(windows)] |
11 | | -use windows::Win32::Security::{GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY}; |
12 | | -#[cfg(windows)] |
13 | | -use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken}; |
14 | | -#[cfg(windows)] |
15 | | -use windows::Win32::UI::Shell::ShellExecuteW; |
16 | | -#[cfg(windows)] |
17 | | -use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; |
18 | | - |
19 | | -/// Check if running as administrator |
20 | | -#[cfg(windows)] |
21 | | -pub fn is_admin() -> bool { |
22 | | - unsafe { |
23 | | - let mut token = HANDLE::default(); |
24 | | - if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).is_ok() { |
25 | | - let mut elevation = TOKEN_ELEVATION::default(); |
26 | | - let mut size = 0u32; |
27 | | - let result = GetTokenInformation( |
28 | | - token, |
29 | | - TokenElevation, |
30 | | - Some(&mut elevation as *mut _ as *mut _), |
31 | | - std::mem::size_of::<TOKEN_ELEVATION>() as u32, |
32 | | - &mut size, |
33 | | - ); |
34 | | - let _ = CloseHandle(token); |
35 | | - result.is_ok() && elevation.TokenIsElevated != 0 |
36 | | - } else { |
37 | | - false |
38 | | - } |
39 | | - } |
40 | | -} |
41 | | - |
42 | | -#[cfg(not(windows))] |
43 | | -pub fn is_admin() -> bool { |
44 | | - false |
45 | | -} |
46 | | - |
47 | | -/// Re-launch the current process with UAC elevation |
48 | | -#[cfg(windows)] |
49 | | -pub fn elevate_with_args(args: &str) -> Result<(), Box<dyn std::error::Error>> { |
50 | | - let exe_path = std::env::current_exe()?; |
51 | | - let exe_path_wide: Vec<u16> = exe_path |
52 | | - .to_string_lossy() |
53 | | - .encode_utf16() |
54 | | - .chain(std::iter::once(0)) |
55 | | - .collect(); |
56 | | - let args_wide: Vec<u16> = format!("{}\0", args).encode_utf16().collect(); |
57 | | - |
58 | | - let result = unsafe { |
59 | | - ShellExecuteW( |
60 | | - Some(HWND::default()), |
61 | | - w!("runas"), |
62 | | - PCWSTR(exe_path_wide.as_ptr()), |
63 | | - PCWSTR(args_wide.as_ptr()), |
64 | | - PCWSTR::null(), |
65 | | - SW_SHOWNORMAL, |
66 | | - ) |
67 | | - }; |
68 | | - |
69 | | - // ShellExecuteW returns > 32 on success |
70 | | - if result.0 as usize > 32 { |
71 | | - Ok(()) |
72 | | - } else { |
73 | | - Err("Failed to elevate privileges".into()) |
74 | | - } |
75 | | -} |
76 | | - |
77 | | -#[cfg(not(windows))] |
78 | | -pub fn elevate_with_args(_args: &str) -> Result<(), Box<dyn std::error::Error>> { |
79 | | - Err("UAC elevation is only supported on Windows".into()) |
80 | | -} |
81 | | - |
82 | 6 | /// Get the installation path for htop |
83 | 7 | pub fn get_install_path() -> Result<PathBuf, Box<dyn std::error::Error>> { |
84 | 8 | let local_app_data = std::env::var("LOCALAPPDATA")?; |
@@ -109,17 +33,8 @@ pub fn get_installed_version() -> Option<String> { |
109 | 33 | } |
110 | 34 |
|
111 | 35 | /// Install htop-win to a PATH directory so it can be run from anywhere |
| 36 | +/// Installs to %LOCALAPPDATA%\Microsoft\WindowsApps which is user-writable and already in PATH |
112 | 37 | pub fn install_to_path(force: bool) -> Result<(), Box<dyn std::error::Error>> { |
113 | | - if !is_admin() { |
114 | | - // Re-launch with UAC elevation |
115 | | - println!("Requesting administrator privileges..."); |
116 | | - let args = if force { "--install --force" } else { "--install" }; |
117 | | - elevate_with_args(args)?; |
118 | | - println!("Elevated process launched. Check that window for results."); |
119 | | - return Ok(()); |
120 | | - } |
121 | | - |
122 | | - // We're running as admin - do the installation |
123 | 38 | let current_exe = std::env::current_exe()?; |
124 | 39 | let current_version = env!("CARGO_PKG_VERSION"); |
125 | 40 | let target_path = get_install_path()?; |
@@ -245,7 +160,6 @@ fn download_file(url: &str, dest: &std::path::Path) -> Result<(), Box<dyn std::e |
245 | 160 | fn cleanup_temp_files() { |
246 | 161 | let temp_dir = std::env::temp_dir(); |
247 | 162 | let _ = fs::remove_file(temp_dir.join("htop-win-update.exe")); |
248 | | - let _ = fs::remove_file(temp_dir.join("htop-win-update-path.txt")); |
249 | 163 | } |
250 | 164 |
|
251 | 165 | /// Update htop-win from GitHub releases |
@@ -281,19 +195,7 @@ pub fn update_from_github(force: bool) -> Result<(), Box<dyn std::error::Error>> |
281 | 195 |
|
282 | 196 | println!("Download complete. Installing..."); |
283 | 197 |
|
284 | | - // Need admin to install to WindowsApps |
285 | | - if !is_admin() { |
286 | | - // Copy temp file path to a location the elevated process can access |
287 | | - let update_marker = temp_dir.join("htop-win-update-path.txt"); |
288 | | - fs::write(&update_marker, temp_file.to_string_lossy().as_bytes())?; |
289 | | - |
290 | | - println!("Requesting administrator privileges..."); |
291 | | - elevate_with_args("--install-update")?; |
292 | | - println!("Elevated process launched. Check that window for results."); |
293 | | - return Ok(()); |
294 | | - } |
295 | | - |
296 | | - // We're admin - do the actual install |
| 198 | + // Install directly - %LOCALAPPDATA%\Microsoft\WindowsApps is user-writable |
297 | 199 | do_install_update(&temp_file) |
298 | 200 | } |
299 | 201 |
|
@@ -340,24 +242,6 @@ pub fn do_install_update(update_file: &std::path::Path) -> Result<(), Box<dyn st |
340 | 242 | Ok(()) |
341 | 243 | } |
342 | 244 |
|
343 | | -/// Complete an update installation (called when elevated with --install-update) |
344 | | -pub fn complete_update_install() -> Result<(), Box<dyn std::error::Error>> { |
345 | | - let temp_dir = std::env::temp_dir(); |
346 | | - let update_marker = temp_dir.join("htop-win-update-path.txt"); |
347 | | - |
348 | | - let update_path = fs::read_to_string(&update_marker)?; |
349 | | - let update_file = PathBuf::from(update_path.trim()); |
350 | | - |
351 | | - // Clean up marker file |
352 | | - let _ = fs::remove_file(&update_marker); |
353 | | - |
354 | | - if !update_file.exists() { |
355 | | - return Err("Update file not found".into()); |
356 | | - } |
357 | | - |
358 | | - do_install_update(&update_file) |
359 | | -} |
360 | | - |
361 | 245 | /// Update status for background updates |
362 | 246 | #[derive(Clone)] |
363 | 247 | pub enum UpdateStatus { |
@@ -418,24 +302,20 @@ pub fn apply_pending_update() -> bool { |
418 | 302 | let temp_dir = std::env::temp_dir(); |
419 | 303 | let update_file = temp_dir.join("htop-win-update.exe"); |
420 | 304 |
|
| 305 | + // Get the currently running executable - this is what we need to update |
| 306 | + let current_exe = match std::env::current_exe() { |
| 307 | + Ok(p) => p, |
| 308 | + Err(_) => return false, |
| 309 | + }; |
| 310 | + |
421 | 311 | if !update_file.exists() { |
422 | 312 | // Clean up any old backup files from previous updates |
423 | | - let install_path = match get_install_path() { |
424 | | - Ok(p) => p, |
425 | | - Err(_) => return false, |
426 | | - }; |
427 | | - let backup_path = install_path.with_extension("exe.old"); |
| 313 | + let backup_path = current_exe.with_extension("exe.old"); |
428 | 314 | let _ = fs::remove_file(&backup_path); |
429 | 315 | return false; |
430 | 316 | } |
431 | 317 |
|
432 | | - let install_path = match get_install_path() { |
433 | | - Ok(p) => p, |
434 | | - Err(_) => { |
435 | | - let _ = fs::remove_file(&update_file); |
436 | | - return false; |
437 | | - } |
438 | | - }; |
| 318 | + let install_path = current_exe; |
439 | 319 |
|
440 | 320 | // If install path doesn't exist, just copy directly |
441 | 321 | if !install_path.exists() { |
|
0 commit comments