Search code examples
windowswinapirustwindows-rs

Replicating Windows Run behavior using either `CreateProcessW` or `ShellExecuteExW`?


The Windows Run dialog (Win+R) is used to launch arbitrary programs from a user-inputted string. What's cool about it is that it can handle spaces in the program path without any escaping.

So for example, this works fine: C:\Program Files\Git\git-bash --cd=./bin. With CMD or Powershell, C:\Program would be interpreted as the program path, and the rest as arguments.

Can this behavior of launching a program from a string that might have spaces in its path be replicated using CreateProcessW or ShellExecuteW? ShellExecuteW needs a separate parameter for the program path and its arguments, which seems tough to work-around with paths that may have spaces. CreateProcessW without passing an lpApplicationName seemed like it could be the solution, but I can't get it working (sample code in Rust below - responses with any lang are fine of course).

use windows::core::PWSTR;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::System::Threading::{
  CreateProcessW, PROCESS_CREATION_FLAGS, PROCESS_INFORMATION,
  STARTUPINFOW,
};

pub fn shell_exec() -> anyhow::Result<()> {
  // Arbitrary command to execute. The command in the variable doesn't work
  // but this path does: r#"C:\Program Files\Git\git-bash"#;
  let command = r#"C:\Program Files\Git\git-bash --cd=./bin"#;

  let command_utf16: Vec<u16> =
    command.encode_utf16().chain(Some(0)).collect();

  let mut si = STARTUPINFOW::default();
  let mut pi = PROCESS_INFORMATION::default();

  unsafe {
    CreateProcessW(
      None,
      PWSTR(command_utf16.as_ptr() as *mut u16),
      None,
      None,
      false,
      PROCESS_CREATION_FLAGS::default(),
      None,
      None,
      &mut si,
      &mut pi,
    )?;

    println!("Command executed successfully.");
    CloseHandle(pi.hProcess)?;
    CloseHandle(pi.hThread)?;
  }

  Ok(())
}

Solution

  • possible use SHEvaluateSystemCommandTemplate

    SHSTDAPI SHEvaluateSystemCommandTemplate(
      _In_ PCWSTR pszCmdTemplate,
      _Out_ PWSTR  *ppszApplication,
      _Out_opt_ PWSTR  *ppszCommandLine,
      _Out_opt_ PWSTR  *ppszParameters
    );
    

    which exactly design for this purpose