]> git.lizzy.rs Git - rust.git/commitdiff
Add std::os::windows::process::CommandExt, with set_creation_flags and add_creation_f...
authorTed Mielczarek <ted@mielczarek.org>
Thu, 1 Dec 2016 00:44:07 +0000 (19:44 -0500)
committerTed Mielczarek <ted@mielczarek.org>
Thu, 1 Dec 2016 00:44:07 +0000 (19:44 -0500)
This adds a CommandExt trait for Windows along with an implementation of it
for std::process::Command with methods to set the process creation flags that
are passed to CreateProcess.

src/libstd/process.rs
src/libstd/sys/windows/ext/process.rs
src/libstd/sys/windows/process.rs

index c99fda9febc245e94d5042a3e015ef65a716aa53..912cc122e9273cd7923e819aa562a630e42fd51a 100644 (file)
@@ -1159,4 +1159,61 @@ fn test_interior_nul_in_env_value_is_error() {
             Ok(_) => panic!(),
         }
     }
+
+    /// Test that process creation flags work by debugging a process.
+    /// Other creation flags make it hard or impossible to detect
+    /// behavioral changes in the process.
+    #[test]
+    #[cfg(windows)]
+    fn test_creation_flags() {
+        use os::windows::process::CommandExt;
+        use sys::c::{BOOL, DWORD, INFINITE};
+        #[repr(C, packed)]
+        struct DEBUG_EVENT {
+            pub event_code: DWORD,
+            pub process_id: DWORD,
+            pub thread_id: DWORD,
+            // This is a union in the real struct, but we don't
+            // need this data for the purposes of this test.
+            pub _junk: [u8; 164],
+        }
+
+        extern "system" {
+            fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
+            fn ContinueDebugEvent(dwProcessId: DWORD, dwThreadId: DWORD, dwContinueStatus: DWORD) -> BOOL;
+        }
+
+        const DEBUG_PROCESS: DWORD = 1;
+        const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
+        const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
+
+        let mut child = Command::new("cmd")
+            .add_creation_flags(DEBUG_PROCESS)
+            .stdin(Stdio::piped()).spawn().unwrap();
+        child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
+        let mut events = 0;
+        let mut event = DEBUG_EVENT {
+            event_code: 0,
+            process_id: 0,
+            thread_id: 0,
+            _junk: [0; 164],
+        };
+        loop {
+            if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
+                panic!("WaitForDebugEvent failed!");
+            }
+            events += 1;
+
+            if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
+                break;
+            }
+
+            if unsafe { ContinueDebugEvent(event.process_id,
+                                           event.thread_id,
+                                           DBG_EXCEPTION_NOT_HANDLED) } == 0 {
+                panic!("ContinueDebugEvent failed!");
+            }
+        }
+        assert!(events > 0);
+    }
 }
index bce32959a23c31f779a71bad76e6f7368f56e466..f5bf3354637cfb82a1023eff437a0a0a9cf086b8 100644 (file)
@@ -15,7 +15,7 @@
 use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle};
 use process;
 use sys;
-use sys_common::{AsInner, FromInner, IntoInner};
+use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl FromRawHandle for process::Stdio {
@@ -97,3 +97,32 @@ fn from_raw(raw: u32) -> Self {
         process::ExitStatus::from_inner(From::from(raw))
     }
 }
+
+/// Windows-specific extensions to the `std::process::Command` builder
+#[unstable(feature = "windows_process_extensions", issue = "37827")]
+pub trait CommandExt {
+    /// Sets the [process creation flags][1] to be passed to `CreateProcess`.
+    ///
+    /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
+    /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
+    #[unstable(feature = "windows_process_extensions", issue = "37827")]
+    fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command;
+    /// Add `flags` to the the [process creation flags][1] to be passed to `CreateProcess`.
+    ///
+    /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
+    /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
+    #[unstable(feature = "windows_process_extensions", issue = "37827")]
+    fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command;
+}
+
+#[unstable(feature = "windows_process_extensions", issue = "37827")]
+impl CommandExt for process::Command {
+    fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command {
+        self.as_inner_mut().set_creation_flags(flags);
+        self
+    }
+    fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command {
+        self.as_inner_mut().add_creation_flags(flags);
+        self
+    }
+}
index d371714ff0e690822d7af30d78cf9948224c5ba9..a221c67efd98914fbbe22d357fc5e14387add0be 100644 (file)
@@ -54,6 +54,7 @@ pub struct Command {
     args: Vec<OsString>,
     env: Option<HashMap<OsString, OsString>>,
     cwd: Option<OsString>,
+    flags: u32,
     detach: bool, // not currently exposed in std::process
     stdin: Option<Stdio>,
     stdout: Option<Stdio>,
@@ -84,6 +85,7 @@ pub fn new(program: &OsStr) -> Command {
             args: Vec::new(),
             env: None,
             cwd: None,
+            flags: 0,
             detach: false,
             stdin: None,
             stdout: None,
@@ -124,6 +126,12 @@ pub fn stdout(&mut self, stdout: Stdio) {
     pub fn stderr(&mut self, stderr: Stdio) {
         self.stderr = Some(stderr);
     }
+    pub fn set_creation_flags(&mut self, flags: u32) {
+        self.flags = flags;
+    }
+    pub fn add_creation_flags(&mut self, flags: u32) {
+        self.flags = self.flags | flags;
+    }
 
     pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                  -> io::Result<(Process, StdioPipes)> {
@@ -157,7 +165,7 @@ pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
         cmd_str.push(0); // add null terminator
 
         // stolen from the libuv code.
-        let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
+        let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT;
         if self.detach {
             flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
         }