Skip to content

Commit

Permalink
Add std::os::windows::process::CommandExt, with set_creation_flags an…
Browse files Browse the repository at this point in the history
…d add_creation_flags methods. Fixes #37827

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.
  • Loading branch information
luser committed Dec 1, 2016
1 parent 127a83d commit 8b1c4cb
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
57 changes: 57 additions & 0 deletions src/libstd/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1159,4 +1159,61 @@ mod tests {
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);
}
}
31 changes: 30 additions & 1 deletion src/libstd/sys/windows/ext/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -97,3 +97,32 @@ impl ExitStatusExt for process::ExitStatus {
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
}
}
10 changes: 9 additions & 1 deletion src/libstd/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -84,6 +85,7 @@ impl Command {
args: Vec::new(),
env: None,
cwd: None,
flags: 0,
detach: false,
stdin: None,
stdout: None,
Expand Down Expand Up @@ -124,6 +126,12 @@ impl Command {
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)> {
Expand Down Expand Up @@ -157,7 +165,7 @@ impl Command {
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;
}
Expand Down

0 comments on commit 8b1c4cb

Please sign in to comment.