diff --git a/ostp-client/src/runner.rs b/ostp-client/src/runner.rs index 7cad9c4..3957e9f 100644 --- a/ostp-client/src/runner.rs +++ b/ostp-client/src/runner.rs @@ -44,31 +44,58 @@ fn is_admin() -> bool { #[cfg(target_os = "windows")] fn relaunch_as_admin() -> Result<()> { - let current_exe = std::env::current_exe()?; - let exe_str = current_exe.to_string_lossy().replace('\'', "''"); - - let current_dir = std::env::current_dir()?; - let dir_str = current_dir.to_string_lossy().replace('\'', "''"); + use std::ffi::OsStr; + use std::os::windows::ffi::OsStrExt; + use std::ptr::null_mut; - let mut arg_parts = Vec::new(); + let exe = std::env::current_exe()?; + let exe_wstr: Vec = exe.as_os_str().encode_wide().chain(Some(0)).collect(); + + let mut args_joined = String::new(); for arg in std::env::args().skip(1) { - arg_parts.push(format!("'{}'", arg.replace('\'', "''"))); + if !args_joined.is_empty() { + args_joined.push(' '); + } + args_joined.push('"'); + args_joined.push_str(&arg.replace('"', "\\\"")); + args_joined.push('"'); + } + let args_wstr: Vec = OsStr::new(&args_joined).encode_wide().chain(Some(0)).collect(); + + let dir = std::env::current_dir()?; + let dir_wstr: Vec = dir.as_os_str().encode_wide().chain(Some(0)).collect(); + + let verb_wstr: Vec = OsStr::new("runas").encode_wide().chain(Some(0)).collect(); + + #[link(name = "shell32")] + extern "system" { + fn ShellExecuteW( + hwnd: *mut std::ffi::c_void, + lpOperation: *const u16, + lpFile: *const u16, + lpParameters: *const u16, + lpDirectory: *const u16, + nShowCmd: i32, + ) -> isize; } - let arg_list_str = if !arg_parts.is_empty() { - format!("-ArgumentList {}", arg_parts.join(",")) - } else { - String::new() - }; + unsafe { + let ret = ShellExecuteW( + null_mut(), + verb_wstr.as_ptr(), + exe_wstr.as_ptr(), + args_wstr.as_ptr(), + dir_wstr.as_ptr(), + 1, // SW_SHOWNORMAL = 1 + ); + if ret <= 32 { + return Err(anyhow::anyhow!( + "Windows UAC Elevation failed or was denied by policy (ShellExecuteW code: {})", + ret + )); + } + } - let ps_script = format!( - "Start-Process -FilePath '{}' {} -WorkingDirectory '{}' -Verb RunAs", - exe_str, arg_list_str, dir_str - ); - - let _ = std::process::Command::new("powershell") - .args(["-Command", &ps_script]) - .status()?; std::process::exit(0); }