Skip to content

Commit bbaa4f5

Browse files
Add new inherit_handles flag to CommandExt trait
This patch adds a new flag to the [`CommandExt`](1) trait to set whether to inherit the handles of the calling process (2) on Windows systems. ACP: rust-lang/libs-team#264 [1]: https://doc.rust-lang.org/stable/std/os/windows/process/trait.CommandExt.html [2]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
1 parent 364da5d commit bbaa4f5

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

library/std/src/os/windows/process.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,17 @@ pub trait CommandExt: Sealed {
365365
/// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
366366
#[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")]
367367
fn startupinfo_force_feedback(&mut self, enabled: Option<bool>) -> &mut process::Command;
368+
369+
/// If this flag is set to `true`, each inheritable handle in the calling process is inherited by the new process.
370+
/// If the flag is `false`, the handles are not inherited.
371+
///
372+
/// The default value for this flag is `true`.
373+
///
374+
/// **Note** that inherited handles have the same value and access rights as the original handles. For additional discussion of inheritable handles, see [Remarks][1].
375+
///
376+
/// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks>
377+
#[unstable(feature = "windows_process_extensions_inherit_handles", issue = "146407")]
378+
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command;
368379
}
369380

370381
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
@@ -421,6 +432,11 @@ impl CommandExt for process::Command {
421432
self.as_inner_mut().startupinfo_force_feedback(enabled);
422433
self
423434
}
435+
436+
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command {
437+
self.as_inner_mut().inherit_handles(inherit_handles);
438+
self
439+
}
424440
}
425441

426442
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]

library/std/src/sys/process/windows.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ pub struct Command {
158158
startupinfo_fullscreen: bool,
159159
startupinfo_untrusted_source: bool,
160160
startupinfo_force_feedback: Option<bool>,
161+
inherit_handles: bool,
161162
}
162163

163164
pub enum Stdio {
@@ -192,6 +193,7 @@ impl Command {
192193
startupinfo_fullscreen: false,
193194
startupinfo_untrusted_source: false,
194195
startupinfo_force_feedback: None,
196+
inherit_handles: true,
195197
}
196198
}
197199

@@ -257,6 +259,10 @@ impl Command {
257259
self.cwd.as_ref().map(Path::new)
258260
}
259261

262+
pub fn inherit_handles(&mut self, inherit_handles: bool) {
263+
self.inherit_handles = inherit_handles;
264+
}
265+
260266
pub fn spawn(
261267
&mut self,
262268
default: Stdio,
@@ -315,6 +321,7 @@ impl Command {
315321
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
316322
}
317323

324+
let inherit_handles = self.inherit_handles as c::BOOL;
318325
let (envp, _data) = make_envp(maybe_env)?;
319326
let (dirp, _data) = make_dirp(self.cwd.as_ref())?;
320327
let mut pi = zeroed_process_information();
@@ -406,7 +413,7 @@ impl Command {
406413
cmd_str.as_mut_ptr(),
407414
ptr::null_mut(),
408415
ptr::null_mut(),
409-
c::TRUE,
416+
inherit_handles,
410417
flags,
411418
envp,
412419
dirp,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Tests `inherit_handles` by spawning a child process and checking the handle
2+
// count of the child process to be 1.
3+
4+
//@ run-pass
5+
//@ only-windows
6+
//@ needs-subprocess
7+
//@ edition: 2024
8+
9+
#![feature(windows_process_extensions_inherit_handles)]
10+
11+
use std::os::windows::io::AsRawHandle;
12+
use std::os::windows::process::CommandExt;
13+
use std::process::{Command, Stdio};
14+
use std::{thread, time};
15+
16+
fn main() {
17+
if std::env::args().skip(1).any(|s| s == "--child") {
18+
child();
19+
} else {
20+
parent();
21+
}
22+
}
23+
24+
fn parent() {
25+
let this_exe = std::env::current_exe().unwrap();
26+
27+
let mut child_proc = Command::new(&this_exe)
28+
.arg("--child")
29+
.stdin(Stdio::piped())
30+
.stdout(Stdio::piped())
31+
.stderr(Stdio::piped())
32+
.inherit_handles(false)
33+
.spawn()
34+
.unwrap();
35+
36+
let mut handle_count = 0;
37+
unsafe {
38+
GetProcessHandleCount(child_proc.as_raw_handle(), &mut handle_count);
39+
}
40+
41+
// Only a single handle to the PE file the process is spawned from is expected at this point.
42+
assert_eq!(handle_count, 1);
43+
44+
// Cleanup.
45+
child_proc.kill().unwrap();
46+
child_proc.wait().unwrap();
47+
}
48+
49+
// A process that stays running until killed.
50+
fn child() {
51+
// Don't wait forever if something goes wrong.
52+
thread::sleep(time::Duration::from_secs(10));
53+
}
54+
55+
// Windows API
56+
mod winapi {
57+
use std::os::windows::raw::HANDLE;
58+
59+
#[link(name = "kernel32")]
60+
unsafe extern "system" {
61+
pub fn GetProcessHandleCount(hprocess: HANDLE, pdwhandlecount: *mut u32) -> i32;
62+
}
63+
}
64+
use winapi::*;

0 commit comments

Comments
 (0)