diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index c223eee95b5f5..b1b02b70e9f63 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -365,6 +365,17 @@ 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) -> &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 [Remarks][1]. + /// + /// [1]: + #[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 +432,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")] diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index f9e15b824757d..2a2e8731269a8 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -158,6 +158,7 @@ pub struct Command { startupinfo_fullscreen: bool, startupinfo_untrusted_source: bool, startupinfo_force_feedback: Option, + inherit_handles: bool, } pub enum Stdio { @@ -192,6 +193,7 @@ impl Command { startupinfo_fullscreen: false, startupinfo_untrusted_source: false, startupinfo_force_feedback: None, + inherit_handles: true, } } @@ -257,6 +259,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, @@ -315,6 +321,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(); @@ -406,7 +413,7 @@ impl Command { cmd_str.as_mut_ptr(), ptr::null_mut(), ptr::null_mut(), - c::TRUE, + inherit_handles, flags, envp, dirp, diff --git a/tests/ui/process/win-inherit-handles.rs b/tests/ui/process/win-inherit-handles.rs new file mode 100644 index 0000000000000..44d31b763990d --- /dev/null +++ b/tests/ui/process/win-inherit-handles.rs @@ -0,0 +1,64 @@ +// Tests `inherit_handles` by spawning a child process and checking the handle +// count of the child process to be 1. + +//@ 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::{thread, time}; + +fn main() { + if std::env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let this_exe = std::env::current_exe().unwrap(); + + let mut child_proc = Command::new(&this_exe) + .arg("--child") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .inherit_handles(false) + .spawn() + .unwrap(); + + let mut handle_count = 0; + unsafe { + GetProcessHandleCount(child_proc.as_raw_handle(), &mut handle_count); + } + + // Only a single handle to the PE file the process is spawned from is expected at this point. + assert_eq!(handle_count, 1); + + // Cleanup. + child_proc.kill().unwrap(); + child_proc.wait().unwrap(); +} + +// A process that stays running until killed. +fn child() { + // Don't wait forever if something goes wrong. + thread::sleep(time::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::*;