@@ -16,6 +16,7 @@ use crate::num::NonZeroI32;
16
16
use crate :: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
17
17
use crate :: os:: windows:: io:: { AsRawHandle , FromRawHandle , IntoRawHandle } ;
18
18
use crate :: path:: { Path , PathBuf } ;
19
+ use crate :: pin:: Pin ;
19
20
use crate :: ptr;
20
21
use crate :: sys:: c;
21
22
use crate :: sys:: c:: NonZeroDWORD ;
@@ -166,6 +167,7 @@ pub struct Command {
166
167
stdout : Option < Stdio > ,
167
168
stderr : Option < Stdio > ,
168
169
force_quotes_enabled : bool ,
170
+ proc_thread_attributes : BTreeMap < usize , ProcThreadAttributeValue > ,
169
171
}
170
172
171
173
pub enum Stdio {
@@ -202,6 +204,7 @@ impl Command {
202
204
stdout : None ,
203
205
stderr : None ,
204
206
force_quotes_enabled : false ,
207
+ proc_thread_attributes : Default :: default ( ) ,
205
208
}
206
209
}
207
210
@@ -251,6 +254,9 @@ impl Command {
251
254
pub fn get_current_dir ( & self ) -> Option < & Path > {
252
255
self . cwd . as_ref ( ) . map ( |cwd| Path :: new ( cwd) )
253
256
}
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
+ }
254
260
255
261
pub fn spawn (
256
262
& mut self ,
@@ -259,9 +265,9 @@ impl Command {
259
265
) -> io:: Result < ( Process , StdioPipes ) > {
260
266
let maybe_env = self . env . capture_if_changed ( ) ;
261
267
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 ;
265
271
266
272
let child_paths = if let Some ( env) = maybe_env. as_ref ( ) {
267
273
env. get ( & EnvKey :: new ( "PATH" ) ) . map ( |s| s. as_os_str ( ) )
@@ -305,9 +311,24 @@ impl Command {
305
311
let stdin = stdin. to_handle ( c:: STD_INPUT_HANDLE , & mut pipes. stdin ) ?;
306
312
let stdout = stdout. to_handle ( c:: STD_OUTPUT_HANDLE , & mut pipes. stdout ) ?;
307
313
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
+ } ) ;
311
332
312
333
let program = to_u16s ( & program) ?;
313
334
unsafe {
@@ -320,7 +341,7 @@ impl Command {
320
341
flags,
321
342
envp,
322
343
dirp,
323
- & mut si,
344
+ & mut si as * mut _ as * mut _ ,
324
345
& mut pi,
325
346
) )
326
347
} ?;
@@ -687,26 +708,29 @@ impl From<u8> for ExitCode {
687
708
}
688
709
}
689
710
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 ( ) ,
710
734
}
711
735
}
712
736
@@ -843,6 +867,57 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
843
867
}
844
868
}
845
869
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
+
846
921
pub struct CommandArgs < ' a > {
847
922
iter : crate :: slice:: Iter < ' a , Arg > ,
848
923
}
0 commit comments