Skip to content

Commit 8b1c4cb

Browse files
committed
Add std::os::windows::process::CommandExt, with set_creation_flags and 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.
1 parent 127a83d commit 8b1c4cb

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

src/libstd/process.rs

+57
Original file line numberDiff line numberDiff line change
@@ -1159,4 +1159,61 @@ mod tests {
11591159
Ok(_) => panic!(),
11601160
}
11611161
}
1162+
1163+
/// Test that process creation flags work by debugging a process.
1164+
/// Other creation flags make it hard or impossible to detect
1165+
/// behavioral changes in the process.
1166+
#[test]
1167+
#[cfg(windows)]
1168+
fn test_creation_flags() {
1169+
use os::windows::process::CommandExt;
1170+
use sys::c::{BOOL, DWORD, INFINITE};
1171+
#[repr(C, packed)]
1172+
struct DEBUG_EVENT {
1173+
pub event_code: DWORD,
1174+
pub process_id: DWORD,
1175+
pub thread_id: DWORD,
1176+
// This is a union in the real struct, but we don't
1177+
// need this data for the purposes of this test.
1178+
pub _junk: [u8; 164],
1179+
}
1180+
1181+
extern "system" {
1182+
fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
1183+
fn ContinueDebugEvent(dwProcessId: DWORD, dwThreadId: DWORD, dwContinueStatus: DWORD) -> BOOL;
1184+
}
1185+
1186+
const DEBUG_PROCESS: DWORD = 1;
1187+
const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
1188+
const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
1189+
1190+
let mut child = Command::new("cmd")
1191+
.add_creation_flags(DEBUG_PROCESS)
1192+
.stdin(Stdio::piped()).spawn().unwrap();
1193+
child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
1194+
let mut events = 0;
1195+
let mut event = DEBUG_EVENT {
1196+
event_code: 0,
1197+
process_id: 0,
1198+
thread_id: 0,
1199+
_junk: [0; 164],
1200+
};
1201+
loop {
1202+
if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
1203+
panic!("WaitForDebugEvent failed!");
1204+
}
1205+
events += 1;
1206+
1207+
if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
1208+
break;
1209+
}
1210+
1211+
if unsafe { ContinueDebugEvent(event.process_id,
1212+
event.thread_id,
1213+
DBG_EXCEPTION_NOT_HANDLED) } == 0 {
1214+
panic!("ContinueDebugEvent failed!");
1215+
}
1216+
}
1217+
assert!(events > 0);
1218+
}
11621219
}

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

+30-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle};
1616
use process;
1717
use sys;
18-
use sys_common::{AsInner, FromInner, IntoInner};
18+
use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
1919

2020
#[stable(feature = "process_extensions", since = "1.2.0")]
2121
impl FromRawHandle for process::Stdio {
@@ -97,3 +97,32 @@ impl ExitStatusExt for process::ExitStatus {
9797
process::ExitStatus::from_inner(From::from(raw))
9898
}
9999
}
100+
101+
/// Windows-specific extensions to the `std::process::Command` builder
102+
#[unstable(feature = "windows_process_extensions", issue = "37827")]
103+
pub trait CommandExt {
104+
/// Sets the [process creation flags][1] to be passed to `CreateProcess`.
105+
///
106+
/// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
107+
/// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
108+
#[unstable(feature = "windows_process_extensions", issue = "37827")]
109+
fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command;
110+
/// Add `flags` to the the [process creation flags][1] to be passed to `CreateProcess`.
111+
///
112+
/// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
113+
/// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
114+
#[unstable(feature = "windows_process_extensions", issue = "37827")]
115+
fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command;
116+
}
117+
118+
#[unstable(feature = "windows_process_extensions", issue = "37827")]
119+
impl CommandExt for process::Command {
120+
fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command {
121+
self.as_inner_mut().set_creation_flags(flags);
122+
self
123+
}
124+
fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command {
125+
self.as_inner_mut().add_creation_flags(flags);
126+
self
127+
}
128+
}

src/libstd/sys/windows/process.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub struct Command {
5454
args: Vec<OsString>,
5555
env: Option<HashMap<OsString, OsString>>,
5656
cwd: Option<OsString>,
57+
flags: u32,
5758
detach: bool, // not currently exposed in std::process
5859
stdin: Option<Stdio>,
5960
stdout: Option<Stdio>,
@@ -84,6 +85,7 @@ impl Command {
8485
args: Vec::new(),
8586
env: None,
8687
cwd: None,
88+
flags: 0,
8789
detach: false,
8890
stdin: None,
8991
stdout: None,
@@ -124,6 +126,12 @@ impl Command {
124126
pub fn stderr(&mut self, stderr: Stdio) {
125127
self.stderr = Some(stderr);
126128
}
129+
pub fn set_creation_flags(&mut self, flags: u32) {
130+
self.flags = flags;
131+
}
132+
pub fn add_creation_flags(&mut self, flags: u32) {
133+
self.flags = self.flags | flags;
134+
}
127135

128136
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
129137
-> io::Result<(Process, StdioPipes)> {
@@ -157,7 +165,7 @@ impl Command {
157165
cmd_str.push(0); // add null terminator
158166

159167
// stolen from the libuv code.
160-
let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
168+
let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT;
161169
if self.detach {
162170
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
163171
}

0 commit comments

Comments
 (0)