std/os/windows/
process.rs

1//! Windows-specific extensions to primitives in the [`std::process`] module.
2//!
3//! [`std::process`]: crate::process
4
5#![stable(feature = "process_extensions", since = "1.2.0")]
6
7use crate::ffi::{OsStr, c_void};
8use crate::mem::MaybeUninit;
9use crate::os::windows::io::{
10    AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
11};
12use crate::sealed::Sealed;
13use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
14use crate::{io, marker, process, ptr, sys};
15
16#[stable(feature = "process_extensions", since = "1.2.0")]
17impl FromRawHandle for process::Stdio {
18    unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio {
19        let handle = unsafe { sys::handle::Handle::from_raw_handle(handle as *mut _) };
20        let io = sys::process::Stdio::Handle(handle);
21        process::Stdio::from_inner(io)
22    }
23}
24
25#[stable(feature = "io_safety", since = "1.63.0")]
26impl From<OwnedHandle> for process::Stdio {
27    /// Takes ownership of a handle and returns a [`Stdio`](process::Stdio)
28    /// that can attach a stream to it.
29    fn from(handle: OwnedHandle) -> process::Stdio {
30        let handle = sys::handle::Handle::from_inner(handle);
31        let io = sys::process::Stdio::Handle(handle);
32        process::Stdio::from_inner(io)
33    }
34}
35
36#[stable(feature = "process_extensions", since = "1.2.0")]
37impl AsRawHandle for process::Child {
38    #[inline]
39    fn as_raw_handle(&self) -> RawHandle {
40        self.as_inner().handle().as_raw_handle() as *mut _
41    }
42}
43
44#[stable(feature = "io_safety", since = "1.63.0")]
45impl AsHandle for process::Child {
46    #[inline]
47    fn as_handle(&self) -> BorrowedHandle<'_> {
48        self.as_inner().handle().as_handle()
49    }
50}
51
52#[stable(feature = "into_raw_os", since = "1.4.0")]
53impl IntoRawHandle for process::Child {
54    fn into_raw_handle(self) -> RawHandle {
55        self.into_inner().into_handle().into_raw_handle() as *mut _
56    }
57}
58
59#[stable(feature = "io_safety", since = "1.63.0")]
60impl From<process::Child> for OwnedHandle {
61    /// Takes ownership of a [`Child`](process::Child)'s process handle.
62    fn from(child: process::Child) -> OwnedHandle {
63        child.into_inner().into_handle().into_inner()
64    }
65}
66
67#[stable(feature = "process_extensions", since = "1.2.0")]
68impl AsRawHandle for process::ChildStdin {
69    #[inline]
70    fn as_raw_handle(&self) -> RawHandle {
71        self.as_inner().handle().as_raw_handle() as *mut _
72    }
73}
74
75#[stable(feature = "process_extensions", since = "1.2.0")]
76impl AsRawHandle for process::ChildStdout {
77    #[inline]
78    fn as_raw_handle(&self) -> RawHandle {
79        self.as_inner().handle().as_raw_handle() as *mut _
80    }
81}
82
83#[stable(feature = "process_extensions", since = "1.2.0")]
84impl AsRawHandle for process::ChildStderr {
85    #[inline]
86    fn as_raw_handle(&self) -> RawHandle {
87        self.as_inner().handle().as_raw_handle() as *mut _
88    }
89}
90
91#[stable(feature = "into_raw_os", since = "1.4.0")]
92impl IntoRawHandle for process::ChildStdin {
93    fn into_raw_handle(self) -> RawHandle {
94        self.into_inner().into_handle().into_raw_handle() as *mut _
95    }
96}
97
98#[stable(feature = "into_raw_os", since = "1.4.0")]
99impl IntoRawHandle for process::ChildStdout {
100    fn into_raw_handle(self) -> RawHandle {
101        self.into_inner().into_handle().into_raw_handle() as *mut _
102    }
103}
104
105#[stable(feature = "into_raw_os", since = "1.4.0")]
106impl IntoRawHandle for process::ChildStderr {
107    fn into_raw_handle(self) -> RawHandle {
108        self.into_inner().into_handle().into_raw_handle() as *mut _
109    }
110}
111
112/// Creates a `ChildStdin` from the provided `OwnedHandle`.
113///
114/// The provided handle must be asynchronous, as reading and
115/// writing from and to it is implemented using asynchronous APIs.
116#[stable(feature = "child_stream_from_fd", since = "1.74.0")]
117impl From<OwnedHandle> for process::ChildStdin {
118    fn from(handle: OwnedHandle) -> process::ChildStdin {
119        let handle = sys::handle::Handle::from_inner(handle);
120        let pipe = sys::pipe::AnonPipe::from_inner(handle);
121        process::ChildStdin::from_inner(pipe)
122    }
123}
124
125/// Creates a `ChildStdout` from the provided `OwnedHandle`.
126///
127/// The provided handle must be asynchronous, as reading and
128/// writing from and to it is implemented using asynchronous APIs.
129#[stable(feature = "child_stream_from_fd", since = "1.74.0")]
130impl From<OwnedHandle> for process::ChildStdout {
131    fn from(handle: OwnedHandle) -> process::ChildStdout {
132        let handle = sys::handle::Handle::from_inner(handle);
133        let pipe = sys::pipe::AnonPipe::from_inner(handle);
134        process::ChildStdout::from_inner(pipe)
135    }
136}
137
138/// Creates a `ChildStderr` from the provided `OwnedHandle`.
139///
140/// The provided handle must be asynchronous, as reading and
141/// writing from and to it is implemented using asynchronous APIs.
142#[stable(feature = "child_stream_from_fd", since = "1.74.0")]
143impl From<OwnedHandle> for process::ChildStderr {
144    fn from(handle: OwnedHandle) -> process::ChildStderr {
145        let handle = sys::handle::Handle::from_inner(handle);
146        let pipe = sys::pipe::AnonPipe::from_inner(handle);
147        process::ChildStderr::from_inner(pipe)
148    }
149}
150
151/// Windows-specific extensions to [`process::ExitStatus`].
152///
153/// This trait is sealed: it cannot be implemented outside the standard library.
154/// This is so that future additional methods are not breaking changes.
155#[stable(feature = "exit_status_from", since = "1.12.0")]
156pub trait ExitStatusExt: Sealed {
157    /// Creates a new `ExitStatus` from the raw underlying `u32` return value of
158    /// a process.
159    #[stable(feature = "exit_status_from", since = "1.12.0")]
160    fn from_raw(raw: u32) -> Self;
161}
162
163#[stable(feature = "exit_status_from", since = "1.12.0")]
164impl ExitStatusExt for process::ExitStatus {
165    fn from_raw(raw: u32) -> Self {
166        process::ExitStatus::from_inner(From::from(raw))
167    }
168}
169
170/// Windows-specific extensions to the [`process::Command`] builder.
171///
172/// This trait is sealed: it cannot be implemented outside the standard library.
173/// This is so that future additional methods are not breaking changes.
174#[stable(feature = "windows_process_extensions", since = "1.16.0")]
175pub trait CommandExt: Sealed {
176    /// Sets the [process creation flags][1] to be passed to `CreateProcess`.
177    ///
178    /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
179    ///
180    /// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
181    #[stable(feature = "windows_process_extensions", since = "1.16.0")]
182    fn creation_flags(&mut self, flags: u32) -> &mut process::Command;
183
184    /// Sets the field `wShowWindow` of [STARTUPINFO][1] that is passed to `CreateProcess`.
185    /// Allowed values are the ones listed in
186    /// <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow>
187    ///
188    /// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow>
189    #[unstable(feature = "windows_process_extensions_show_window", issue = "127544")]
190    fn show_window(&mut self, cmd_show: u16) -> &mut process::Command;
191
192    /// Forces all arguments to be wrapped in quote (`"`) characters.
193    ///
194    /// This is useful for passing arguments to [MSYS2/Cygwin][1] based
195    /// executables: these programs will expand unquoted arguments containing
196    /// wildcard characters (`?` and `*`) by searching for any file paths
197    /// matching the wildcard pattern.
198    ///
199    /// Adding quotes has no effect when passing arguments to programs
200    /// that use [msvcrt][2]. This includes programs built with both
201    /// MinGW and MSVC.
202    ///
203    /// [1]: <https://github.com/msys2/MSYS2-packages/issues/2176>
204    /// [2]: <https://msdn.microsoft.com/en-us/library/17w5ykft.aspx>
205    #[unstable(feature = "windows_process_extensions_force_quotes", issue = "82227")]
206    fn force_quotes(&mut self, enabled: bool) -> &mut process::Command;
207
208    /// Append literal text to the command line without any quoting or escaping.
209    ///
210    /// This is useful for passing arguments to applications that don't follow
211    /// the standard C run-time escaping rules, such as `cmd.exe /c`.
212    ///
213    /// # Batch files
214    ///
215    /// Note the `cmd /c` command line has slightly different escaping rules than batch files
216    /// themselves. If possible, it may be better to write complex arguments to a temporary
217    /// `.bat` file, with appropriate escaping, and simply run that using:
218    ///
219    /// ```no_run
220    /// # use std::process::Command;
221    /// # let temp_bat_file = "";
222    /// # #[allow(unused)]
223    /// let output = Command::new("cmd").args(["/c", &format!("\"{temp_bat_file}\"")]).output();
224    /// ```
225    ///
226    /// # Example
227    ///
228    /// Run a batch script using both trusted and untrusted arguments.
229    ///
230    /// ```no_run
231    /// #[cfg(windows)]
232    /// // `my_script_path` is a path to known bat file.
233    /// // `user_name` is an untrusted name given by the user.
234    /// fn run_script(
235    ///     my_script_path: &str,
236    ///     user_name: &str,
237    /// ) -> Result<std::process::Output, std::io::Error> {
238    ///     use std::io::{Error, ErrorKind};
239    ///     use std::os::windows::process::CommandExt;
240    ///     use std::process::Command;
241    ///
242    ///     // Create the command line, making sure to quote the script path.
243    ///     // This assumes the fixed arguments have been tested to work with the script we're using.
244    ///     let mut cmd_args = format!(r#""{my_script_path}" "--features=[a,b,c]""#);
245    ///
246    ///     // Make sure the user name is safe. In particular we need to be
247    ///     // cautious of ascii symbols that cmd may interpret specially.
248    ///     // Here we only allow alphanumeric characters.
249    ///     if !user_name.chars().all(|c| c.is_alphanumeric()) {
250    ///         return Err(Error::new(ErrorKind::InvalidInput, "invalid user name"));
251    ///     }
252    ///
253    ///     // now we have validated the user name, let's add that too.
254    ///     cmd_args.push_str(" --user ");
255    ///     cmd_args.push_str(user_name);
256    ///
257    ///     // call cmd.exe and return the output
258    ///     Command::new("cmd.exe")
259    ///         .arg("/c")
260    ///         // surround the entire command in an extra pair of quotes, as required by cmd.exe.
261    ///         .raw_arg(&format!("\"{cmd_args}\""))
262    ///         .output()
263    /// }
264    /// ````
265    #[stable(feature = "windows_process_extensions_raw_arg", since = "1.62.0")]
266    fn raw_arg<S: AsRef<OsStr>>(&mut self, text_to_append_as_is: S) -> &mut process::Command;
267
268    /// When [`process::Command`] creates pipes, request that our side is always async.
269    ///
270    /// By default [`process::Command`] may choose to use pipes where both ends
271    /// are opened for synchronous read or write operations. By using
272    /// `async_pipes(true)`, this behavior is overridden so that our side is
273    /// always async.
274    ///
275    /// This is important because if doing async I/O a pipe or a file has to be
276    /// opened for async access.
277    ///
278    /// The end of the pipe sent to the child process will always be synchronous
279    /// regardless of this option.
280    ///
281    /// # Example
282    ///
283    /// ```
284    /// #![feature(windows_process_extensions_async_pipes)]
285    /// use std::os::windows::process::CommandExt;
286    /// use std::process::{Command, Stdio};
287    ///
288    /// # let program = "";
289    ///
290    /// Command::new(program)
291    ///     .async_pipes(true)
292    ///     .stdin(Stdio::piped())
293    ///     .stdout(Stdio::piped())
294    ///     .stderr(Stdio::piped());
295    /// ```
296    #[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")]
297    fn async_pipes(&mut self, always_async: bool) -> &mut process::Command;
298
299    /// Executes the command as a child process with the given
300    /// [`ProcThreadAttributeList`], returning a handle to it.
301    ///
302    /// This method enables the customization of attributes for the spawned
303    /// child process on Windows systems.
304    /// Attributes offer extended configurability for process creation,
305    /// but their usage can be intricate and potentially unsafe.
306    ///
307    /// # Note
308    ///
309    /// By default, stdin, stdout, and stderr are inherited from the parent
310    /// process.
311    ///
312    /// # Example
313    ///
314    /// ```
315    /// #![feature(windows_process_extensions_raw_attribute)]
316    /// use std::os::windows::io::AsRawHandle;
317    /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
318    /// use std::process::Command;
319    ///
320    /// # struct ProcessDropGuard(std::process::Child);
321    /// # impl Drop for ProcessDropGuard {
322    /// #     fn drop(&mut self) {
323    /// #         let _ = self.0.kill();
324    /// #     }
325    /// # }
326    /// #
327    /// let parent = Command::new("cmd").spawn()?;
328    /// let parent_process_handle = parent.as_raw_handle();
329    /// # let parent = ProcessDropGuard(parent);
330    ///
331    /// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
332    /// let mut attribute_list = ProcThreadAttributeList::build()
333    ///     .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
334    ///     .finish()
335    ///     .unwrap();
336    ///
337    /// let mut child = Command::new("cmd").spawn_with_attributes(&attribute_list)?;
338    /// #
339    /// # child.kill()?;
340    /// # Ok::<(), std::io::Error>(())
341    /// ```
342    #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
343    fn spawn_with_attributes(
344        &mut self,
345        attribute_list: &ProcThreadAttributeList<'_>,
346    ) -> io::Result<process::Child>;
347
348    /// When true, sets the `STARTF_RUNFULLSCREEN` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`.
349    ///
350    /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
351    #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")]
352    fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command;
353
354    /// When true, sets the `STARTF_UNTRUSTEDSOURCE` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`.
355    ///
356    /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
357    #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")]
358    fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command;
359
360    /// When specified, sets the following flags on the [STARTUPINFO][1] struct before passing it to `CreateProcess`:
361    /// - If `Some(true)`, sets `STARTF_FORCEONFEEDBACK`
362    /// - If `Some(false)`, sets `STARTF_FORCEOFFFEEDBACK`
363    /// - If `None`, does not set any flags
364    ///
365    /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
366    #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")]
367    fn startupinfo_force_feedback(&mut self, enabled: Option<bool>) -> &mut process::Command;
368}
369
370#[stable(feature = "windows_process_extensions", since = "1.16.0")]
371impl CommandExt for process::Command {
372    fn creation_flags(&mut self, flags: u32) -> &mut process::Command {
373        self.as_inner_mut().creation_flags(flags);
374        self
375    }
376
377    fn show_window(&mut self, cmd_show: u16) -> &mut process::Command {
378        self.as_inner_mut().show_window(Some(cmd_show));
379        self
380    }
381
382    fn force_quotes(&mut self, enabled: bool) -> &mut process::Command {
383        self.as_inner_mut().force_quotes(enabled);
384        self
385    }
386
387    fn raw_arg<S: AsRef<OsStr>>(&mut self, raw_text: S) -> &mut process::Command {
388        self.as_inner_mut().raw_arg(raw_text.as_ref());
389        self
390    }
391
392    fn async_pipes(&mut self, always_async: bool) -> &mut process::Command {
393        // FIXME: This currently has an intentional no-op implementation.
394        // For the time being our side of the pipes will always be async.
395        // Once the ecosystem has adjusted, we may then be able to start making
396        // use of synchronous pipes within the standard library.
397        let _ = always_async;
398        self
399    }
400
401    fn spawn_with_attributes(
402        &mut self,
403        attribute_list: &ProcThreadAttributeList<'_>,
404    ) -> io::Result<process::Child> {
405        self.as_inner_mut()
406            .spawn_with_attributes(sys::process::Stdio::Inherit, true, Some(attribute_list))
407            .map(process::Child::from_inner)
408    }
409
410    fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command {
411        self.as_inner_mut().startupinfo_fullscreen(enabled);
412        self
413    }
414
415    fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command {
416        self.as_inner_mut().startupinfo_untrusted_source(enabled);
417        self
418    }
419
420    fn startupinfo_force_feedback(&mut self, enabled: Option<bool>) -> &mut process::Command {
421        self.as_inner_mut().startupinfo_force_feedback(enabled);
422        self
423    }
424}
425
426#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
427pub trait ChildExt: Sealed {
428    /// Extracts the main thread raw handle, without taking ownership
429    #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
430    fn main_thread_handle(&self) -> BorrowedHandle<'_>;
431}
432
433#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
434impl ChildExt for process::Child {
435    fn main_thread_handle(&self) -> BorrowedHandle<'_> {
436        self.handle.main_thread_handle()
437    }
438}
439
440/// Windows-specific extensions to [`process::ExitCode`].
441///
442/// This trait is sealed: it cannot be implemented outside the standard library.
443/// This is so that future additional methods are not breaking changes.
444#[unstable(feature = "windows_process_exit_code_from", issue = "111688")]
445pub trait ExitCodeExt: Sealed {
446    /// Creates a new `ExitCode` from the raw underlying `u32` return value of
447    /// a process.
448    ///
449    /// The exit code should not be 259, as this conflicts with the `STILL_ACTIVE`
450    /// macro returned from the `GetExitCodeProcess` function to signal that the
451    /// process has yet to run to completion.
452    #[unstable(feature = "windows_process_exit_code_from", issue = "111688")]
453    fn from_raw(raw: u32) -> Self;
454}
455
456#[unstable(feature = "windows_process_exit_code_from", issue = "111688")]
457impl ExitCodeExt for process::ExitCode {
458    fn from_raw(raw: u32) -> Self {
459        process::ExitCode::from_inner(From::from(raw))
460    }
461}
462
463/// A wrapper around windows [`ProcThreadAttributeList`][1].
464///
465/// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist>
466#[derive(Debug)]
467#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
468pub struct ProcThreadAttributeList<'a> {
469    attribute_list: Box<[MaybeUninit<u8>]>,
470    _lifetime_marker: marker::PhantomData<&'a ()>,
471}
472
473#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
474impl<'a> ProcThreadAttributeList<'a> {
475    /// Creates a new builder for constructing a [`ProcThreadAttributeList`].
476    pub fn build() -> ProcThreadAttributeListBuilder<'a> {
477        ProcThreadAttributeListBuilder::new()
478    }
479
480    /// Returns a pointer to the underling attribute list.
481    #[doc(hidden)]
482    pub fn as_ptr(&self) -> *const MaybeUninit<u8> {
483        self.attribute_list.as_ptr()
484    }
485}
486
487#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
488impl<'a> Drop for ProcThreadAttributeList<'a> {
489    /// Deletes the attribute list.
490    ///
491    /// This method calls [`DeleteProcThreadAttributeList`][1] to delete the
492    /// underlying attribute list.
493    ///
494    /// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-deleteprocthreadattributelist>
495    fn drop(&mut self) {
496        let lp_attribute_list = self.attribute_list.as_mut_ptr().cast::<c_void>();
497        unsafe { sys::c::DeleteProcThreadAttributeList(lp_attribute_list) }
498    }
499}
500
501/// Builder for constructing a [`ProcThreadAttributeList`].
502#[derive(Clone, Debug)]
503#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
504pub struct ProcThreadAttributeListBuilder<'a> {
505    attributes: alloc::collections::BTreeMap<usize, ProcThreadAttributeValue>,
506    _lifetime_marker: marker::PhantomData<&'a ()>,
507}
508
509#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")]
510impl<'a> ProcThreadAttributeListBuilder<'a> {
511    fn new() -> Self {
512        ProcThreadAttributeListBuilder {
513            attributes: alloc::collections::BTreeMap::new(),
514            _lifetime_marker: marker::PhantomData,
515        }
516    }
517
518    /// Sets an attribute on the attribute list.
519    ///
520    /// The `attribute` parameter specifies the raw attribute to be set, while
521    /// the `value` parameter holds the value associated with that attribute.
522    /// Please refer to the [Windows documentation][1] for a list of valid attributes.
523    ///
524    /// # Note
525    ///
526    /// The maximum number of attributes is the value of [`u32::MAX`]. If this
527    /// limit is exceeded, the call to [`Self::finish`] will return an `Error`
528    /// indicating that the maximum number of attributes has been exceeded.
529    ///
530    /// # Safety Note
531    ///
532    /// Remember that improper use of attributes can lead to undefined behavior
533    /// or security vulnerabilities. Always consult the documentation and ensure
534    /// proper attribute values are used.
535    ///
536    /// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute#parameters>
537    pub fn attribute<T>(self, attribute: usize, value: &'a T) -> Self {
538        unsafe {
539            self.raw_attribute(attribute, ptr::addr_of!(*value).cast::<c_void>(), size_of::<T>())
540        }
541    }
542
543    /// Sets a raw attribute on the attribute list.
544    ///
545    /// This function is useful for setting attributes with pointers or sizes
546    /// that cannot be derived directly from their values.
547    ///
548    /// # Safety
549    ///
550    /// This function is marked as `unsafe` because it deals with raw pointers
551    /// and sizes. It is the responsibility of the caller to ensure the value
552    /// lives longer than the resulting [`ProcThreadAttributeList`] as well as
553    /// the validity of the size parameter.
554    ///
555    /// # Example
556    ///
557    /// ```
558    /// #![feature(windows_process_extensions_raw_attribute)]
559    /// use std::ffi::c_void;
560    /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
561    /// use std::os::windows::raw::HANDLE;
562    /// use std::process::Command;
563    ///
564    /// #[repr(C)]
565    /// pub struct COORD {
566    ///     pub X: i16,
567    ///     pub Y: i16,
568    /// }
569    ///
570    /// unsafe extern "system" {
571    ///     fn CreatePipe(
572    ///         hreadpipe: *mut HANDLE,
573    ///         hwritepipe: *mut HANDLE,
574    ///         lppipeattributes: *const c_void,
575    ///         nsize: u32,
576    ///     ) -> i32;
577    ///     fn CreatePseudoConsole(
578    ///         size: COORD,
579    ///         hinput: HANDLE,
580    ///         houtput: HANDLE,
581    ///         dwflags: u32,
582    ///         phpc: *mut isize,
583    ///     ) -> i32;
584    ///     fn CloseHandle(hobject: HANDLE) -> i32;
585    /// }
586    ///
587    /// let [mut input_read_side, mut output_write_side, mut output_read_side, mut input_write_side] =
588    ///     [unsafe { std::mem::zeroed::<HANDLE>() }; 4];
589    ///
590    /// unsafe {
591    ///     CreatePipe(&mut input_read_side, &mut input_write_side, std::ptr::null(), 0);
592    ///     CreatePipe(&mut output_read_side, &mut output_write_side, std::ptr::null(), 0);
593    /// }
594    ///
595    /// let size = COORD { X: 60, Y: 40 };
596    /// let mut h_pc = unsafe { std::mem::zeroed() };
597    /// unsafe { CreatePseudoConsole(size, input_read_side, output_write_side, 0, &mut h_pc) };
598    ///
599    /// unsafe { CloseHandle(input_read_side) };
600    /// unsafe { CloseHandle(output_write_side) };
601    ///
602    /// const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: usize = 131094;
603    ///
604    /// let attribute_list = unsafe {
605    ///     ProcThreadAttributeList::build()
606    ///         .raw_attribute(
607    ///             PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
608    ///             h_pc as *const c_void,
609    ///             size_of::<isize>(),
610    ///         )
611    ///         .finish()?
612    /// };
613    ///
614    /// let mut child = Command::new("cmd").spawn_with_attributes(&attribute_list)?;
615    /// #
616    /// # child.kill()?;
617    /// # Ok::<(), std::io::Error>(())
618    /// ```
619    pub unsafe fn raw_attribute<T>(
620        mut self,
621        attribute: usize,
622        value_ptr: *const T,
623        value_size: usize,
624    ) -> Self {
625        self.attributes.insert(
626            attribute,
627            ProcThreadAttributeValue { ptr: value_ptr.cast::<c_void>(), size: value_size },
628        );
629        self
630    }
631
632    /// Finalizes the construction of the `ProcThreadAttributeList`.
633    ///
634    /// # Errors
635    ///
636    /// Returns an error if the maximum number of attributes is exceeded
637    /// or if there is an I/O error during initialization.
638    pub fn finish(&self) -> io::Result<ProcThreadAttributeList<'a>> {
639        // To initialize our ProcThreadAttributeList, we need to determine
640        // how many bytes to allocate for it. The Windows API simplifies this
641        // process by allowing us to call `InitializeProcThreadAttributeList`
642        // with a null pointer to retrieve the required size.
643        let mut required_size = 0;
644        let Ok(attribute_count) = self.attributes.len().try_into() else {
645            return Err(io::const_error!(
646                io::ErrorKind::InvalidInput,
647                "maximum number of ProcThreadAttributes exceeded",
648            ));
649        };
650        unsafe {
651            sys::c::InitializeProcThreadAttributeList(
652                ptr::null_mut(),
653                attribute_count,
654                0,
655                &mut required_size,
656            )
657        };
658
659        let mut attribute_list = vec![MaybeUninit::uninit(); required_size].into_boxed_slice();
660
661        // Once we've allocated the necessary memory, it's safe to invoke
662        // `InitializeProcThreadAttributeList` to properly initialize the list.
663        sys::cvt(unsafe {
664            sys::c::InitializeProcThreadAttributeList(
665                attribute_list.as_mut_ptr().cast::<c_void>(),
666                attribute_count,
667                0,
668                &mut required_size,
669            )
670        })?;
671
672        // # Add our attributes to the buffer.
673        // It's theoretically possible for the attribute count to exceed a u32
674        // value. Therefore, we ensure that we don't add more attributes than
675        // the buffer was initialized for.
676        for (&attribute, value) in self.attributes.iter().take(attribute_count as usize) {
677            sys::cvt(unsafe {
678                sys::c::UpdateProcThreadAttribute(
679                    attribute_list.as_mut_ptr().cast::<c_void>(),
680                    0,
681                    attribute,
682                    value.ptr,
683                    value.size,
684                    ptr::null_mut(),
685                    ptr::null_mut(),
686                )
687            })?;
688        }
689
690        Ok(ProcThreadAttributeList { attribute_list, _lifetime_marker: marker::PhantomData })
691    }
692}
693
694/// Wrapper around the value data to be used as a Process Thread Attribute.
695#[derive(Clone, Debug)]
696struct ProcThreadAttributeValue {
697    ptr: *const c_void,
698    size: usize,
699}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy