@@ -16,6 +16,7 @@ use crate::num::NonZeroI32;
1616use crate :: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
1717use crate :: os:: windows:: io:: { AsRawHandle , FromRawHandle , IntoRawHandle } ;
1818use crate :: path:: { Path , PathBuf } ;
19+ use crate :: pin:: Pin ;
1920use crate :: ptr;
2021use crate :: sys:: c;
2122use crate :: sys:: c:: NonZeroDWORD ;
@@ -166,6 +167,7 @@ pub struct Command {
166167 stdout : Option < Stdio > ,
167168 stderr : Option < Stdio > ,
168169 force_quotes_enabled : bool ,
170+ proc_thread_attributes : BTreeMap < usize , ProcThreadAttributeValue > ,
169171}
170172
171173pub enum Stdio {
@@ -202,6 +204,7 @@ impl Command {
202204 stdout : None ,
203205 stderr : None ,
204206 force_quotes_enabled : false ,
207+ proc_thread_attributes : Default :: default ( ) ,
205208 }
206209 }
207210
@@ -251,6 +254,9 @@ impl Command {
251254 pub fn get_current_dir ( & self ) -> Option < & Path > {
252255 self . cwd . as_ref ( ) . map ( |cwd| Path :: new ( cwd) )
253256 }
257+ pub fn process_thread_attribute ( & mut self , attribute : usize , ptr : * mut c_void , size : usize ) {
258+ self . proc_thread_attributes . insert ( attribute, ProcThreadAttributeValue { ptr, size } ) ;
259+ }
254260
255261 pub fn spawn (
256262 & mut self ,
@@ -259,9 +265,9 @@ impl Command {
259265 ) -> io:: Result < ( Process , StdioPipes ) > {
260266 let maybe_env = self . env . capture_if_changed ( ) ;
261267
262- let mut si = zeroed_startupinfo ( ) ;
263- si. cb = mem:: size_of :: < c:: STARTUPINFO > ( ) as c:: DWORD ;
264- si. dwFlags = c:: STARTF_USESTDHANDLES ;
268+ let mut si = zeroed_startupinfo_ex ( ) ;
269+ si. StartupInfo . cb = mem:: size_of :: < c:: STARTUPINFO > ( ) as c:: DWORD ;
270+ si. StartupInfo . dwFlags = c:: STARTF_USESTDHANDLES ;
265271
266272 let child_paths = if let Some ( env) = maybe_env. as_ref ( ) {
267273 env. get ( & EnvKey :: new ( "PATH" ) ) . map ( |s| s. as_os_str ( ) )
@@ -305,9 +311,24 @@ impl Command {
305311 let stdin = stdin. to_handle ( c:: STD_INPUT_HANDLE , & mut pipes. stdin ) ?;
306312 let stdout = stdout. to_handle ( c:: STD_OUTPUT_HANDLE , & mut pipes. stdout ) ?;
307313 let stderr = stderr. to_handle ( c:: STD_ERROR_HANDLE , & mut pipes. stderr ) ?;
308- si. hStdInput = stdin. as_raw_handle ( ) ;
309- si. hStdOutput = stdout. as_raw_handle ( ) ;
310- si. hStdError = stderr. as_raw_handle ( ) ;
314+ si. StartupInfo . hStdInput = stdin. as_raw_handle ( ) ;
315+ si. StartupInfo . hStdOutput = stdout. as_raw_handle ( ) ;
316+ si. StartupInfo . hStdError = stderr. as_raw_handle ( ) ;
317+
318+ let mut attributes = if !self . proc_thread_attributes . is_empty ( ) {
319+ Some ( make_proc_thread_attributes ( & self . proc_thread_attributes ) ?)
320+ } else {
321+ None
322+ } ;
323+ si. lpAttributeList = attributes. as_mut ( ) . map_or ( ptr:: null_mut ( ) , |buf| {
324+ // Indicate that lpAttributeList is present by setting
325+ // EXTENDED_STARTUPINFO_PRESENT and adjusting the size
326+ // value of the struct.
327+ flags |= c:: EXTENDED_STARTUPINFO_PRESENT ;
328+ si. StartupInfo . cb = mem:: size_of :: < c:: STARTUPINFOEX > ( ) as u32 ;
329+
330+ buf. 0 . as_mut_ptr ( ) . cast ( )
331+ } ) ;
311332
312333 let program = to_u16s ( & program) ?;
313334 unsafe {
@@ -320,7 +341,7 @@ impl Command {
320341 flags,
321342 envp,
322343 dirp,
323- & mut si,
344+ & mut si as * mut _ as * mut _ ,
324345 & mut pi,
325346 ) )
326347 } ?;
@@ -687,26 +708,29 @@ impl From<u8> for ExitCode {
687708 }
688709}
689710
690- fn zeroed_startupinfo ( ) -> c:: STARTUPINFO {
691- c:: STARTUPINFO {
692- cb : 0 ,
693- lpReserved : ptr:: null_mut ( ) ,
694- lpDesktop : ptr:: null_mut ( ) ,
695- lpTitle : ptr:: null_mut ( ) ,
696- dwX : 0 ,
697- dwY : 0 ,
698- dwXSize : 0 ,
699- dwYSize : 0 ,
700- dwXCountChars : 0 ,
701- dwYCountCharts : 0 ,
702- dwFillAttribute : 0 ,
703- dwFlags : 0 ,
704- wShowWindow : 0 ,
705- cbReserved2 : 0 ,
706- lpReserved2 : ptr:: null_mut ( ) ,
707- hStdInput : c:: INVALID_HANDLE_VALUE ,
708- hStdOutput : c:: INVALID_HANDLE_VALUE ,
709- hStdError : c:: INVALID_HANDLE_VALUE ,
711+ fn zeroed_startupinfo_ex ( ) -> c:: STARTUPINFOEX {
712+ c:: STARTUPINFOEX {
713+ StartupInfo : c:: STARTUPINFO {
714+ cb : 0 ,
715+ lpReserved : ptr:: null_mut ( ) ,
716+ lpDesktop : ptr:: null_mut ( ) ,
717+ lpTitle : ptr:: null_mut ( ) ,
718+ dwX : 0 ,
719+ dwY : 0 ,
720+ dwXSize : 0 ,
721+ dwYSize : 0 ,
722+ dwXCountChars : 0 ,
723+ dwYCountCharts : 0 ,
724+ dwFillAttribute : 0 ,
725+ dwFlags : 0 ,
726+ wShowWindow : 0 ,
727+ cbReserved2 : 0 ,
728+ lpReserved2 : ptr:: null_mut ( ) ,
729+ hStdInput : c:: INVALID_HANDLE_VALUE ,
730+ hStdOutput : c:: INVALID_HANDLE_VALUE ,
731+ hStdError : c:: INVALID_HANDLE_VALUE ,
732+ } ,
733+ lpAttributeList : ptr:: null_mut ( ) ,
710734 }
711735}
712736
@@ -843,6 +867,57 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
843867 }
844868}
845869
870+ /// Wrapper around a u8 buffer which ensures that a ProcThreadAttributeList
871+ /// is not moved during use and is not freed before calling Delete.
872+ struct ProcThreadAttributeList ( Pin < Vec < u8 > > ) ;
873+ impl Drop for ProcThreadAttributeList {
874+ fn drop ( & mut self ) {
875+ unsafe { c:: DeleteProcThreadAttributeList ( self . 0 . as_mut_ptr ( ) . cast ( ) ) }
876+ }
877+ }
878+ /// Wrapper around the value data to be used as a Process Thread Attribute.
879+ /// This exists primarily to force the raw pointer type to be `Send` and `Sync`
880+ /// without needing to `unsafe impl` them for `Command`.
881+ #[ derive( Copy , Clone ) ]
882+ struct ProcThreadAttributeValue {
883+ ptr : * mut c_void ,
884+ size : usize ,
885+ }
886+ unsafe impl Send for ProcThreadAttributeValue { }
887+ unsafe impl Sync for ProcThreadAttributeValue { }
888+
889+ fn make_proc_thread_attributes (
890+ attributes : & BTreeMap < usize , ProcThreadAttributeValue > ,
891+ ) -> io:: Result < ProcThreadAttributeList > {
892+ let mut buffer_size = 0 ;
893+ let count = attributes. len ( ) as u32 ;
894+ // First call to get required size. Error is expected.
895+ unsafe { c:: InitializeProcThreadAttributeList ( ptr:: null_mut ( ) , count, 0 , & mut buffer_size) } ;
896+ let mut buf = Pin :: new ( crate :: vec:: from_elem ( 0u8 , buffer_size as usize ) ) ;
897+ // Second call to really initialize the buffer.
898+ cvt ( unsafe {
899+ c:: InitializeProcThreadAttributeList ( buf. as_mut_ptr ( ) . cast ( ) , count, 0 , & mut buffer_size)
900+ } ) ?;
901+ let mut attribute_list = ProcThreadAttributeList ( buf) ;
902+ // Add our attributes to the buffer.
903+ // It's theoretically possible for the count to overflow a u32. Therefore, make
904+ // sure we don't add more attributes than we actually initialized the buffer for.
905+ for ( & attribute, & value) in attributes. iter ( ) . take ( count as usize ) {
906+ cvt ( unsafe {
907+ c:: UpdateProcThreadAttribute (
908+ attribute_list. 0 . as_mut_ptr ( ) . cast ( ) ,
909+ 0 ,
910+ attribute,
911+ value. ptr ,
912+ value. size ,
913+ ptr:: null_mut ( ) ,
914+ ptr:: null_mut ( ) ,
915+ )
916+ } ) ?;
917+ }
918+ Ok ( attribute_list)
919+ }
920+
846921pub struct CommandArgs < ' a > {
847922 iter : crate :: slice:: Iter < ' a , Arg > ,
848923}
0 commit comments