@@ -6,12 +6,14 @@ use crate::file::{open, OpenOptions};
6
6
use crate :: handle:: Handle ;
7
7
use crate :: path_ext;
8
8
use crate :: pipe:: { self , AnonPipe } ;
9
- use crate :: { c, windows } ;
9
+ use crate :: { c, util } ;
10
10
use cvt:: cvt;
11
11
use std:: collections:: BTreeMap ;
12
12
use std:: env:: consts:: { EXE_EXTENSION , EXE_SUFFIX } ;
13
13
use std:: ffi:: { c_void, OsStr , OsString } ;
14
14
use std:: fs:: File ;
15
+ use std:: io:: ErrorKind ;
16
+ use std:: mem:: MaybeUninit ;
15
17
use std:: os:: windows:: prelude:: {
16
18
AsRawHandle , FromRawHandle , IntoRawHandle , OsStrExt , OsStringExt , RawHandle ,
17
19
} ;
@@ -45,6 +47,7 @@ pub struct Command {
45
47
stdout : Option < Stdio > ,
46
48
stderr : Option < Stdio > ,
47
49
force_quotes_enabled : bool ,
50
+ proc_thread_attributes : BTreeMap < usize , ProcThreadAttributeValue > ,
48
51
}
49
52
50
53
impl Command {
@@ -60,6 +63,7 @@ impl Command {
60
63
stdout : None ,
61
64
stderr : None ,
62
65
force_quotes_enabled : false ,
66
+ proc_thread_attributes : Default :: default ( ) ,
63
67
}
64
68
}
65
69
@@ -115,6 +119,17 @@ impl Command {
115
119
// self.env.iter()
116
120
// }
117
121
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
+
118
133
pub fn get_current_dir ( & self ) -> Option < & Path > {
119
134
self . cwd . as_ref ( ) . map ( Path :: new)
120
135
}
@@ -191,7 +206,6 @@ impl Command {
191
206
let stderr = stderr. to_handle ( c:: STD_ERROR_HANDLE , & mut pipes. stderr ) ?;
192
207
193
208
let mut si = zeroed_startupinfo ( ) ;
194
- si. cb = mem:: size_of :: < c:: STARTUPINFOW > ( ) as c:: DWORD ;
195
209
196
210
// If at least one of stdin, stdout or stderr are set (i.e. are non null)
197
211
// then set the `hStd` fields in `STARTUPINFO`.
@@ -205,6 +219,27 @@ impl Command {
205
219
si. hStdError = stderr. as_raw_handle ( ) ;
206
220
}
207
221
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
+
208
243
unsafe {
209
244
cvt ( c:: CreateProcessW (
210
245
program. as_ptr ( ) ,
@@ -215,7 +250,7 @@ impl Command {
215
250
flags,
216
251
envp,
217
252
dirp,
218
- & si ,
253
+ si_ptr ,
219
254
& mut pi,
220
255
) )
221
256
} ?;
@@ -339,7 +374,7 @@ pub struct CommandArgs<'a> {
339
374
340
375
// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
341
376
fn 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 (
343
378
|buf, size| unsafe { c:: GetSystemDirectoryW ( buf, size) } ,
344
379
|buf| buf. into ( ) ,
345
380
) ?;
@@ -557,15 +592,15 @@ where
557
592
// 3 & 4. System paths
558
593
// SAFETY: This uses `fill_utf16_buf` to safely call the OS functions.
559
594
unsafe {
560
- if let Ok ( Some ( path) ) = windows :: fill_utf16_buf (
595
+ if let Ok ( Some ( path) ) = util :: fill_utf16_buf (
561
596
|buf, size| c:: GetSystemDirectoryW ( buf, size) ,
562
597
|buf| exists ( PathBuf :: from ( OsString :: from_wide ( buf) ) ) ,
563
598
) {
564
599
return Some ( path) ;
565
600
}
566
601
#[ cfg( not( target_vendor = "uwp" ) ) ]
567
602
{
568
- if let Ok ( Some ( path) ) = windows :: fill_utf16_buf (
603
+ if let Ok ( Some ( path) ) = util :: fill_utf16_buf (
569
604
|buf, size| c:: GetWindowsDirectoryW ( buf, size) ,
570
605
|buf| exists ( PathBuf :: from ( OsString :: from_wide ( buf) ) ) ,
571
606
) {
@@ -600,3 +635,79 @@ fn program_exists(path: &Path) -> Option<Vec<u16>> {
600
635
}
601
636
}
602
637
}
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