Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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(())
}
like image 315
L. Berger Avatar asked Oct 30 '25 03:10

L. Berger


2 Answers

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

like image 114
RbMm Avatar answered Nov 01 '25 17:11

RbMm


The documentation you linked says:

The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string

So let command = r#"C:\Program Files\Git\git-bash --cd=./bin"#; would be interpreted as the command C:\Program with the parameters Files\Git\git-bash and --cd=./bin

The run dialog does it's own parsing of the input (it can also accept URL/URI or any real paths for example) which you have to replicate.

like image 34
cafce25 Avatar answered Nov 01 '25 16:11

cafce25