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}