Auto merge of #38098 - luser:windows-commandext, r=alexcrichton
Add std::os::windows::process::CommandExt. 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.
This commit is contained in:
@@ -1167,4 +1167,62 @@ mod tests {
|
|||||||
Ok(_) => panic!(),
|
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")
|
||||||
|
.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle};
|
use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle};
|
||||||
use process;
|
use process;
|
||||||
use sys;
|
use sys;
|
||||||
use sys_common::{AsInner, FromInner, IntoInner};
|
use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
|
||||||
|
|
||||||
#[stable(feature = "process_extensions", since = "1.2.0")]
|
#[stable(feature = "process_extensions", since = "1.2.0")]
|
||||||
impl FromRawHandle for process::Stdio {
|
impl FromRawHandle for process::Stdio {
|
||||||
@@ -97,3 +97,22 @@ impl ExitStatusExt for process::ExitStatus {
|
|||||||
process::ExitStatus::from_inner(From::from(raw))
|
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 creation_flags(&mut self, flags: u32) -> &mut process::Command;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "windows_process_extensions", issue = "37827")]
|
||||||
|
impl CommandExt for process::Command {
|
||||||
|
fn creation_flags(&mut self, flags: u32) -> &mut process::Command {
|
||||||
|
self.as_inner_mut().creation_flags(flags);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ pub struct Command {
|
|||||||
args: Vec<OsString>,
|
args: Vec<OsString>,
|
||||||
env: Option<HashMap<OsString, OsString>>,
|
env: Option<HashMap<OsString, OsString>>,
|
||||||
cwd: Option<OsString>,
|
cwd: Option<OsString>,
|
||||||
|
flags: u32,
|
||||||
detach: bool, // not currently exposed in std::process
|
detach: bool, // not currently exposed in std::process
|
||||||
stdin: Option<Stdio>,
|
stdin: Option<Stdio>,
|
||||||
stdout: Option<Stdio>,
|
stdout: Option<Stdio>,
|
||||||
@@ -84,6 +85,7 @@ impl Command {
|
|||||||
args: Vec::new(),
|
args: Vec::new(),
|
||||||
env: None,
|
env: None,
|
||||||
cwd: None,
|
cwd: None,
|
||||||
|
flags: 0,
|
||||||
detach: false,
|
detach: false,
|
||||||
stdin: None,
|
stdin: None,
|
||||||
stdout: None,
|
stdout: None,
|
||||||
@@ -124,6 +126,9 @@ impl Command {
|
|||||||
pub fn stderr(&mut self, stderr: Stdio) {
|
pub fn stderr(&mut self, stderr: Stdio) {
|
||||||
self.stderr = Some(stderr);
|
self.stderr = Some(stderr);
|
||||||
}
|
}
|
||||||
|
pub fn creation_flags(&mut self, flags: u32) {
|
||||||
|
self.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
|
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
|
||||||
-> io::Result<(Process, StdioPipes)> {
|
-> io::Result<(Process, StdioPipes)> {
|
||||||
@@ -157,7 +162,7 @@ impl Command {
|
|||||||
cmd_str.push(0); // add null terminator
|
cmd_str.push(0); // add null terminator
|
||||||
|
|
||||||
// stolen from the libuv code.
|
// stolen from the libuv code.
|
||||||
let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
|
let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT;
|
||||||
if self.detach {
|
if self.detach {
|
||||||
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
|
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user