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: https://github.com/rust-lang/libs-team/issues/264 [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw>
This commit is contained in:
commit
e4bf8b3fc3
3 changed files with 108 additions and 1 deletions
|
|
@ -365,6 +365,20 @@ pub trait CommandExt: Sealed {
|
|||
/// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
|
||||
#[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")]
|
||||
fn startupinfo_force_feedback(&mut self, enabled: Option<bool>) -> &mut process::Command;
|
||||
|
||||
/// If this flag is set to `true`, each inheritable handle in the calling
|
||||
/// process is inherited by the new process. If the flag is `false`, the
|
||||
/// handles are not inherited.
|
||||
///
|
||||
/// The default value for this flag is `true`.
|
||||
///
|
||||
/// **Note** that inherited handles have the same value and access rights
|
||||
/// as the original handles. For additional discussion of inheritable handles,
|
||||
/// see the [Remarks][1] section of the `CreateProcessW` documentation.
|
||||
///
|
||||
/// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks
|
||||
#[unstable(feature = "windows_process_extensions_inherit_handles", issue = "146407")]
|
||||
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command;
|
||||
}
|
||||
|
||||
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
|
||||
|
|
@ -421,6 +435,11 @@ impl CommandExt for process::Command {
|
|||
self.as_inner_mut().startupinfo_force_feedback(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command {
|
||||
self.as_inner_mut().inherit_handles(inherit_handles);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ pub struct Command {
|
|||
startupinfo_fullscreen: bool,
|
||||
startupinfo_untrusted_source: bool,
|
||||
startupinfo_force_feedback: Option<bool>,
|
||||
inherit_handles: bool,
|
||||
}
|
||||
|
||||
pub enum Stdio {
|
||||
|
|
@ -187,6 +188,7 @@ impl Command {
|
|||
startupinfo_fullscreen: false,
|
||||
startupinfo_untrusted_source: false,
|
||||
startupinfo_force_feedback: None,
|
||||
inherit_handles: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,6 +254,10 @@ impl Command {
|
|||
self.cwd.as_ref().map(Path::new)
|
||||
}
|
||||
|
||||
pub fn inherit_handles(&mut self, inherit_handles: bool) {
|
||||
self.inherit_handles = inherit_handles;
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&mut self,
|
||||
default: Stdio,
|
||||
|
|
@ -310,6 +316,7 @@ impl Command {
|
|||
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
|
||||
}
|
||||
|
||||
let inherit_handles = self.inherit_handles as c::BOOL;
|
||||
let (envp, _data) = make_envp(maybe_env)?;
|
||||
let (dirp, _data) = make_dirp(self.cwd.as_ref())?;
|
||||
let mut pi = zeroed_process_information();
|
||||
|
|
@ -401,7 +408,7 @@ impl Command {
|
|||
cmd_str.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
c::TRUE,
|
||||
inherit_handles,
|
||||
flags,
|
||||
envp,
|
||||
dirp,
|
||||
|
|
|
|||
81
tests/ui/process/win-inherit-handles.rs
Normal file
81
tests/ui/process/win-inherit-handles.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// Tests `inherit_handles` by spawning a child process and checking its handle
|
||||
// count to be greater than when not setting the option.
|
||||
|
||||
//@ run-pass
|
||||
//@ only-windows
|
||||
//@ needs-subprocess
|
||||
//@ edition: 2024
|
||||
|
||||
#![feature(windows_process_extensions_inherit_handles)]
|
||||
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::Duration;
|
||||
use std::{env, io, thread};
|
||||
|
||||
fn main() {
|
||||
if std::env::args().skip(1).any(|s| s == "--child") {
|
||||
child();
|
||||
} else {
|
||||
parent();
|
||||
}
|
||||
}
|
||||
|
||||
fn parent() {
|
||||
let with_inherit_count = child_handle_count(true);
|
||||
let without_inherit_count = child_handle_count(false);
|
||||
// Only compare the two values instead of only expecting a hard 1 for
|
||||
// robustness, although only 1 has ever been observed here.
|
||||
assert!(
|
||||
with_inherit_count > without_inherit_count,
|
||||
"Child process handle count unexpectedly smaller when inheriting handles compared to when \
|
||||
not: {} <= {}",
|
||||
with_inherit_count,
|
||||
without_inherit_count,
|
||||
);
|
||||
}
|
||||
|
||||
/// Spawns the current program as a child process and returns its handle count.
|
||||
fn child_handle_count(inherit_handles: bool) -> u32 {
|
||||
let mut child_proc = Command::new(&env::current_exe().unwrap())
|
||||
.arg("--child")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.inherit_handles(inherit_handles)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let mut handle_count = 0;
|
||||
let ret = unsafe { GetProcessHandleCount(child_proc.as_raw_handle(), &raw mut handle_count) };
|
||||
assert_ne!(
|
||||
ret,
|
||||
0,
|
||||
"GetProcessHandleCount failed: {:?}",
|
||||
io::Error::last_os_error(),
|
||||
);
|
||||
|
||||
// Cleanup.
|
||||
child_proc.kill().unwrap();
|
||||
child_proc.wait().unwrap();
|
||||
|
||||
handle_count
|
||||
}
|
||||
|
||||
/// A process that stays running until killed.
|
||||
fn child() {
|
||||
// Don't wait forever if something goes wrong.
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
}
|
||||
|
||||
// Windows API
|
||||
mod winapi {
|
||||
use std::os::windows::raw::HANDLE;
|
||||
|
||||
#[link(name = "kernel32")]
|
||||
unsafe extern "system" {
|
||||
pub fn GetProcessHandleCount(hprocess: HANDLE, pdwhandlecount: *mut u32) -> i32;
|
||||
}
|
||||
}
|
||||
use winapi::*;
|
||||
Loading…
Add table
Add a link
Reference in a new issue