Skip to content

Commit e4bf8b3

Browse files
authored
Rollup merge of #115501 - michaelvanstraten:set_inherit_handles, r=ChrisDenton
Add new inherit_handles flag to CommandExt trait This PR adds a new flag to the [`CommandExt`](https://doc.rust-lang.org/stable/std/os/windows/process/trait.CommandExt.html) trait to set whether to inherit the handles of the calling process ([ref][1]). This is necessary when, for example, spawning a process with a `pseudoconsole` attached. r? ``@ChrisDenton`` ACP: rust-lang/libs-team#264 [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw>
2 parents 1d23d06 + 24b0c27 commit e4bf8b3

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,20 @@ 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
370+
/// process is inherited by the new process. If the flag is `false`, the
371+
/// handles are not inherited.
372+
///
373+
/// The default value for this flag is `true`.
374+
///
375+
/// **Note** that inherited handles have the same value and access rights
376+
/// as the original handles. For additional discussion of inheritable handles,
377+
/// see the [Remarks][1] section of the `CreateProcessW` documentation.
378+
///
379+
/// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks
380+
#[unstable(feature = "windows_process_extensions_inherit_handles", issue = "146407")]
381+
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command;
368382
}
369383

370384
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
@@ -421,6 +435,11 @@ impl CommandExt for process::Command {
421435
self.as_inner_mut().startupinfo_force_feedback(enabled);
422436
self
423437
}
438+
439+
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command {
440+
self.as_inner_mut().inherit_handles(inherit_handles);
441+
self
442+
}
424443
}
425444

426445
#[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
@@ -159,6 +159,7 @@ pub struct Command {
159159
startupinfo_fullscreen: bool,
160160
startupinfo_untrusted_source: bool,
161161
startupinfo_force_feedback: Option<bool>,
162+
inherit_handles: bool,
162163
}
163164

164165
pub enum Stdio {
@@ -187,6 +188,7 @@ impl Command {
187188
startupinfo_fullscreen: false,
188189
startupinfo_untrusted_source: false,
189190
startupinfo_force_feedback: None,
191+
inherit_handles: true,
190192
}
191193
}
192194

@@ -252,6 +254,10 @@ impl Command {
252254
self.cwd.as_ref().map(Path::new)
253255
}
254256

257+
pub fn inherit_handles(&mut self, inherit_handles: bool) {
258+
self.inherit_handles = inherit_handles;
259+
}
260+
255261
pub fn spawn(
256262
&mut self,
257263
default: Stdio,
@@ -310,6 +316,7 @@ impl Command {
310316
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
311317
}
312318

319+
let inherit_handles = self.inherit_handles as c::BOOL;
313320
let (envp, _data) = make_envp(maybe_env)?;
314321
let (dirp, _data) = make_dirp(self.cwd.as_ref())?;
315322
let mut pi = zeroed_process_information();
@@ -401,7 +408,7 @@ impl Command {
401408
cmd_str.as_mut_ptr(),
402409
ptr::null_mut(),
403410
ptr::null_mut(),
404-
c::TRUE,
411+
inherit_handles,
405412
flags,
406413
envp,
407414
dirp,
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Tests `inherit_handles` by spawning a child process and checking its handle
2+
// count to be greater than when not setting the option.
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::time::Duration;
15+
use std::{env, io, thread};
16+
17+
fn main() {
18+
if std::env::args().skip(1).any(|s| s == "--child") {
19+
child();
20+
} else {
21+
parent();
22+
}
23+
}
24+
25+
fn parent() {
26+
let with_inherit_count = child_handle_count(true);
27+
let without_inherit_count = child_handle_count(false);
28+
// Only compare the two values instead of only expecting a hard 1 for
29+
// robustness, although only 1 has ever been observed here.
30+
assert!(
31+
with_inherit_count > without_inherit_count,
32+
"Child process handle count unexpectedly smaller when inheriting handles compared to when \
33+
not: {} <= {}",
34+
with_inherit_count,
35+
without_inherit_count,
36+
);
37+
}
38+
39+
/// Spawns the current program as a child process and returns its handle count.
40+
fn child_handle_count(inherit_handles: bool) -> u32 {
41+
let mut child_proc = Command::new(&env::current_exe().unwrap())
42+
.arg("--child")
43+
.stdin(Stdio::piped())
44+
.stdout(Stdio::piped())
45+
.stderr(Stdio::piped())
46+
.inherit_handles(inherit_handles)
47+
.spawn()
48+
.unwrap();
49+
50+
let mut handle_count = 0;
51+
let ret = unsafe { GetProcessHandleCount(child_proc.as_raw_handle(), &raw mut handle_count) };
52+
assert_ne!(
53+
ret,
54+
0,
55+
"GetProcessHandleCount failed: {:?}",
56+
io::Error::last_os_error(),
57+
);
58+
59+
// Cleanup.
60+
child_proc.kill().unwrap();
61+
child_proc.wait().unwrap();
62+
63+
handle_count
64+
}
65+
66+
/// A process that stays running until killed.
67+
fn child() {
68+
// Don't wait forever if something goes wrong.
69+
thread::sleep(Duration::from_secs(10));
70+
}
71+
72+
// Windows API
73+
mod winapi {
74+
use std::os::windows::raw::HANDLE;
75+
76+
#[link(name = "kernel32")]
77+
unsafe extern "system" {
78+
pub fn GetProcessHandleCount(hprocess: HANDLE, pdwhandlecount: *mut u32) -> i32;
79+
}
80+
}
81+
use winapi::*;

0 commit comments

Comments
 (0)