@@ -6,12 +6,14 @@ use crate::file::{open, OpenOptions};
66use crate :: handle:: Handle ;
77use crate :: path_ext;
88use crate :: pipe:: { self , AnonPipe } ;
9- use crate :: { c, windows } ;
9+ use crate :: { c, util } ;
1010use cvt:: cvt;
1111use std:: collections:: BTreeMap ;
1212use std:: env:: consts:: { EXE_EXTENSION , EXE_SUFFIX } ;
1313use std:: ffi:: { c_void, OsStr , OsString } ;
1414use std:: fs:: File ;
15+ use std:: io:: ErrorKind ;
16+ use std:: mem:: MaybeUninit ;
1517use std:: os:: windows:: prelude:: {
1618 AsRawHandle , FromRawHandle , IntoRawHandle , OsStrExt , OsStringExt , RawHandle ,
1719} ;
@@ -45,6 +47,7 @@ pub struct Command {
4547 stdout : Option < Stdio > ,
4648 stderr : Option < Stdio > ,
4749 force_quotes_enabled : bool ,
50+ proc_thread_attributes : BTreeMap < usize , ProcThreadAttributeValue > ,
4851}
4952
5053impl Command {
@@ -60,6 +63,7 @@ impl Command {
6063 stdout : None ,
6164 stderr : None ,
6265 force_quotes_enabled : false ,
66+ proc_thread_attributes : Default :: default ( ) ,
6367 }
6468 }
6569
@@ -115,6 +119,17 @@ impl Command {
115119 // self.env.iter()
116120 // }
117121
122+ pub unsafe fn raw_attribute < T : Copy + Send + Sync + ' static > (
123+ & mut self ,
124+ attribute : usize ,
125+ value : T ,
126+ ) {
127+ self . proc_thread_attributes . insert (
128+ attribute,
129+ ProcThreadAttributeValue { size : mem:: size_of :: < T > ( ) , data : Box :: new ( value) } ,
130+ ) ;
131+ }
132+
118133 pub fn get_current_dir ( & self ) -> Option < & Path > {
119134 self . cwd . as_ref ( ) . map ( Path :: new)
120135 }
@@ -191,7 +206,6 @@ impl Command {
191206 let stderr = stderr. to_handle ( c:: STD_ERROR_HANDLE , & mut pipes. stderr ) ?;
192207
193208 let mut si = zeroed_startupinfo ( ) ;
194- si. cb = mem:: size_of :: < c:: STARTUPINFOW > ( ) as c:: DWORD ;
195209
196210 // If at least one of stdin, stdout or stderr are set (i.e. are non null)
197211 // then set the `hStd` fields in `STARTUPINFO`.
@@ -205,6 +219,27 @@ impl Command {
205219 si. hStdError = stderr. as_raw_handle ( ) ;
206220 }
207221
222+ let si_ptr: * mut c:: STARTUPINFOW ;
223+
224+ let mut proc_thread_attribute_list;
225+ let mut si_ex;
226+
227+ if !self . proc_thread_attributes . is_empty ( ) {
228+ si. cb = mem:: size_of :: < c:: STARTUPINFOEXW > ( ) as u32 ;
229+ flags |= c:: EXTENDED_STARTUPINFO_PRESENT ;
230+
231+ proc_thread_attribute_list =
232+ make_proc_thread_attribute_list ( & self . proc_thread_attributes ) ?;
233+ si_ex = c:: STARTUPINFOEXW {
234+ StartupInfo : si,
235+ lpAttributeList : proc_thread_attribute_list. 0 . as_mut_ptr ( ) as _ ,
236+ } ;
237+ si_ptr = & mut si_ex as * mut _ as _ ;
238+ } else {
239+ si. cb = mem:: size_of :: < c:: STARTUPINFOW > as c:: DWORD ;
240+ si_ptr = & mut si as * mut _ as _ ;
241+ }
242+
208243 unsafe {
209244 cvt ( c:: CreateProcessW (
210245 program. as_ptr ( ) ,
@@ -215,7 +250,7 @@ impl Command {
215250 flags,
216251 envp,
217252 dirp,
218- & si ,
253+ si_ptr ,
219254 & mut pi,
220255 ) )
221256 } ?;
@@ -339,7 +374,7 @@ pub struct CommandArgs<'a> {
339374
340375// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
341376fn command_prompt ( ) -> io:: Result < Vec < u16 > > {
342- let mut system: Vec < u16 > = windows :: fill_utf16_buf (
377+ let mut system: Vec < u16 > = util :: fill_utf16_buf (
343378 |buf, size| unsafe { c:: GetSystemDirectoryW ( buf, size) } ,
344379 |buf| buf. into ( ) ,
345380 ) ?;
@@ -557,15 +592,15 @@ where
557592 // 3 & 4. System paths
558593 // SAFETY: This uses `fill_utf16_buf` to safely call the OS functions.
559594 unsafe {
560- if let Ok ( Some ( path) ) = windows :: fill_utf16_buf (
595+ if let Ok ( Some ( path) ) = util :: fill_utf16_buf (
561596 |buf, size| c:: GetSystemDirectoryW ( buf, size) ,
562597 |buf| exists ( PathBuf :: from ( OsString :: from_wide ( buf) ) ) ,
563598 ) {
564599 return Some ( path) ;
565600 }
566601 #[ cfg( not( target_vendor = "uwp" ) ) ]
567602 {
568- if let Ok ( Some ( path) ) = windows :: fill_utf16_buf (
603+ if let Ok ( Some ( path) ) = util :: fill_utf16_buf (
569604 |buf, size| c:: GetWindowsDirectoryW ( buf, size) ,
570605 |buf| exists ( PathBuf :: from ( OsString :: from_wide ( buf) ) ) ,
571606 ) {
@@ -600,3 +635,79 @@ fn program_exists(path: &Path) -> Option<Vec<u16>> {
600635 }
601636 }
602637}
638+
639+
640+ struct ProcThreadAttributeList ( Box < [ MaybeUninit < u8 > ] > ) ;
641+
642+ impl Drop for ProcThreadAttributeList {
643+ fn drop ( & mut self ) {
644+ let lp_attribute_list = self . 0 . as_mut_ptr ( ) as _ ;
645+ unsafe { c:: DeleteProcThreadAttributeList ( lp_attribute_list) }
646+ }
647+ }
648+
649+ /// Wrapper around the value data to be used as a Process Thread Attribute.
650+ struct ProcThreadAttributeValue {
651+ data : Box < dyn Send + Sync > ,
652+ size : usize ,
653+ }
654+
655+ fn make_proc_thread_attribute_list (
656+ attributes : & BTreeMap < usize , ProcThreadAttributeValue > ,
657+ ) -> io:: Result < ProcThreadAttributeList > {
658+ // To initialize our ProcThreadAttributeList, we need to determine
659+ // how many bytes to allocate for it. The Windows API simplifies this process
660+ // by allowing us to call `InitializeProcThreadAttributeList` with
661+ // a null pointer to retrieve the required size.
662+ let mut required_size = 0 ;
663+ let Ok ( attribute_count) = attributes. len ( ) . try_into ( ) else {
664+ return Err ( io:: Error :: new (
665+ ErrorKind :: InvalidInput ,
666+ "maximum number of ProcThreadAttributes exceeded" ,
667+ ) ) ;
668+ } ;
669+ unsafe {
670+ c:: InitializeProcThreadAttributeList (
671+ ptr:: null_mut ( ) ,
672+ attribute_count,
673+ 0 ,
674+ & mut required_size,
675+ )
676+ } ;
677+
678+ let mut proc_thread_attribute_list = ProcThreadAttributeList (
679+ vec ! [ MaybeUninit :: uninit( ) ; required_size as usize ] . into_boxed_slice ( ) ,
680+ ) ;
681+
682+ // Once we've allocated the necessary memory, it's safe to invoke
683+ // `InitializeProcThreadAttributeList` to properly initialize the list.
684+ cvt ( unsafe {
685+ c:: InitializeProcThreadAttributeList (
686+ proc_thread_attribute_list. 0 . as_mut_ptr ( ) as * mut _ ,
687+ attribute_count,
688+ 0 ,
689+ & mut required_size,
690+ )
691+ } ) ?;
692+
693+ // # Add our attributes to the buffer.
694+ // It's theoretically possible for the attribute count to exceed a u32 value.
695+ // Therefore, we ensure that we don't add more attributes than the buffer was initialized for.
696+ for ( & attribute, value) in attributes. iter ( ) . take ( attribute_count as usize ) {
697+ let value_ptr = & * value. data as * const ( dyn Send + Sync ) as _ ;
698+ cvt ( unsafe {
699+ c:: UpdateProcThreadAttribute (
700+ proc_thread_attribute_list. 0 . as_mut_ptr ( ) as _ ,
701+ 0 ,
702+ attribute,
703+ value_ptr,
704+ value. size ,
705+ ptr:: null_mut ( ) ,
706+ ptr:: null_mut ( ) ,
707+ )
708+ } ) ?;
709+ }
710+
711+ Ok ( proc_thread_attribute_list)
712+ }
713+
0 commit comments