elevated_command/windows.rs
1/*---------------------------------------------------------------------------------------------
2 * Copyright (c) Luis Liu. All rights reserved.
3 * Licensed under the MIT License. See License in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5
6use crate::Command;
7use anyhow::Result;
8use std::mem;
9use std::os::windows::process::ExitStatusExt;
10use std::process::{Output, ExitStatus};
11use winapi::shared::minwindef::{DWORD, LPVOID};
12use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
13use winapi::um::securitybaseapi::GetTokenInformation;
14use winapi::um::winnt::{HANDLE, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY};
15use windows::core::{HSTRING, PCWSTR, w};
16use windows::Win32::Foundation::HWND;
17use windows::Win32::UI::Shell::ShellExecuteW;
18use windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
19
20
21/// The implementation of state check and elevated executing varies on each platform
22impl Command {
23 /// Check the state the current program running
24 ///
25 /// Return `true` if the program is running as root, otherwise false
26 ///
27 /// # Examples
28 ///
29 /// ```no_run
30 /// use elevated_command::Command;
31 ///
32 /// fn main() {
33 /// let is_elevated = Command::is_elevated();
34 ///
35 /// }
36 /// ```
37 pub fn is_elevated() -> bool {
38 // Thanks to https://stackoverflow.com/a/8196291
39 unsafe {
40 let mut current_token_ptr: HANDLE = mem::zeroed();
41 let mut token_elevation: TOKEN_ELEVATION = mem::zeroed();
42 let token_elevation_type_ptr: *mut TOKEN_ELEVATION = &mut token_elevation;
43 let mut size: DWORD = 0;
44
45 let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut current_token_ptr);
46
47 if result != 0 {
48 let result = GetTokenInformation(
49 current_token_ptr,
50 TokenElevation,
51 token_elevation_type_ptr as LPVOID,
52 mem::size_of::<winapi::um::winnt::TOKEN_ELEVATION_TYPE>() as u32,
53 &mut size,
54 );
55 if result != 0 {
56 return token_elevation.TokenIsElevated != 0;
57 }
58 }
59 }
60 false
61 }
62
63 /// Prompting the user with a graphical OS dialog for the root password,
64 /// excuting the command with escalated privileges, and return the output
65 ///
66 /// On Windows, according to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew#return-value,
67 /// Output.status.code() shoudl be greater than 32 if the function succeeds,
68 /// otherwise the value indicates the cause of the failure
69 ///
70 /// On Windows, Output.stdout and Output.stderr will always be empty as of now
71 ///
72 /// # Examples
73 ///
74 /// ```no_run
75 /// use elevated_command::Command;
76 /// use std::process::Command as StdCommand;
77 ///
78 /// fn main() {
79 /// let mut cmd = StdCommand::new("path to the application");
80 /// let elevated_cmd = Command::new(cmd);
81 /// let output = elevated_cmd.output().unwrap();
82 /// }
83 /// ```
84 pub fn output(&self) -> Result<Output> {
85 let args = self.cmd.get_args()
86 .map(|c| c.to_str().unwrap().to_string())
87 .collect::<Vec<String>>();
88 let parameters = if args.is_empty() {
89 HSTRING::new()
90 } else {
91 let arg_str = args.join(" ");
92 HSTRING::from(arg_str)
93 };
94
95 // according to https://stackoverflow.com/a/38034535
96 // the cwd always point to %SystemRoot%\System32 and cannot be changed by settting lpdirectory param
97 let r = unsafe {
98 ShellExecuteW(
99 HWND(0),
100 w!("runas"),
101 &HSTRING::from(self.cmd.get_program()),
102 &HSTRING::from(parameters),
103 PCWSTR::null(),
104 SW_HIDE
105 )
106 };
107 Ok(Output {
108 status: ExitStatus::from_raw(r.0 as u32),
109 stdout: Vec::<u8>::new(),
110 stderr: Vec::<u8>::new(),
111 })
112 }
113}