How to use Rust to invoke a program as another user without calling terminal

I need to use Rust to call the Windows package manager (winget) to install some file. The install process require administrative privilege; therefore I have to use Powershell as another user.
So basically, from Rust, I have to call powershell as another user, then pass the command "winget install..."
Is there a better way to do this, like not needing to use Powershell or something?
Thank you.

You can use ShellExecuteExW from the windows crate to execute a command as an administrator and wait for it to finish, instead of going via PowerShell (which calls this anyway). Here's a standalone example; you'd want better error handling:

use std::mem::size_of;
use std::process::exit;

use windows::core::Error;
use windows::imp::WaitForSingleObject;
use windows::w;
use windows::Win32::Foundation::{WAIT_OBJECT_0, CloseHandle};
use windows::Win32::System::Threading::{GetExitCodeProcess, INFINITE};
use windows::Win32::UI::Shell::{ShellExecuteExW, SEE_MASK_NOCLOSEPROCESS, SHELLEXECUTEINFOW};
use windows::Win32::UI::WindowsAndMessaging::SW_NORMAL;

fn main() {
    let mut sh_exec_info = SHELLEXECUTEINFOW {
        cbSize: size_of::<SHELLEXECUTEINFOW>() as u32,
        fMask: SEE_MASK_NOCLOSEPROCESS,
        lpVerb: w!("runas"),
        lpFile: w!("winget.exe"),
        lpParameters: w!("upgrade"),
        nShow: SW_NORMAL.0 as i32,
        ..SHELLEXECUTEINFOW::default()
    };
    if let Err(e) = unsafe { ShellExecuteExW(&mut sh_exec_info).ok() } {
        eprintln!("ShellExecuteExW: {e:?}");
        exit(1);
    }
    println!("Waiting for winget to finish...");
    let r = unsafe { WaitForSingleObject(sh_exec_info.hProcess.0, INFINITE) };
    if r != WAIT_OBJECT_0.0 {
        eprintln!("WaitForSingleObject: {:?}", Error::from_win32());
        exit(1);
    }
    let mut status = 0u32;
    if let Err(e) = unsafe { GetExitCodeProcess(sh_exec_info.hProcess, &mut status).ok() } {
        eprintln!("GetExitCodeProcess: {e:?}");
        exit(1);
    }
    unsafe { CloseHandle(sh_exec_info.hProcess); }
    println!("Completed with status {}.", status);
}

This code should also try harder to call CloseHandle on error paths, possibly by converting it to an OwnedHandle or by wrapping it in a new type with its own Drop implementation.

1 Like

Wow this is fast. Awesome. Thank you.

Though one more thing, I have to run this as another user. Is there anyway to pass other user credential to this?

Thank you.

If this is the same as Start-Process -Credential, then you can do the same as PowerShell does, by calling the CreateProcessWithLogonW API instead. You can find the C# code for what PowerShell does in its StartWithCreateProcess function.

I haven't used this API myself and I think my PC is configured not to allow passwords, so I can't give you a Rust example of this as easily, I'm afraid.

If your requirements are more complicated, you might find it easiest to make your Rust program run as an administrator first, by specifying level="requireAdministrator" in its manifest or by re-running itself with ShellExecuteW. That depends on what your requirements are, though.

1 Like

I guess something I will try to figure this out. Thank you a lot though. Have a nice day.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.