use crate::*;
use crate::process::environment::*;
use abistr::{TryIntoAsOptCStr, AsOptCStr};
use winapi::shared::minwindef::{BOOL, LPVOID, DWORD};
use winapi::shared::ntdef::{LPCSTR, LPSTR, HANDLE};
use winapi::shared::winerror::*;
use winapi::um::minwinbase::LPSECURITY_ATTRIBUTES;
use winapi::um::processthreadsapi::*;
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::*;
#[cfg(std)] use std::ffi::OsStr;
#[cfg(std)] use std::os::windows::prelude::*;
#[cfg(std)] use std::path::Path;
#[cfg(std)] use std::vec::Vec;
use core::ptr::{null_mut, null};
#[cfg(std)]
pub fn argv_to_command_line_0<A: AsRef<OsStr>>(exe: impl AsRef<Path>, args: impl IntoIterator<Item = A>) -> Vec<u16> {
let mut cl = Vec::new();
argv_to_command_line_0_inplace(exe, args, &mut cl);
cl
}
#[cfg(std)]
pub fn exe_to_command_line_0(exe: impl AsRef<Path>) -> Vec<u16> {
let args : [&'static str; 0] = [];
argv_to_command_line_0(exe, args)
}
#[cfg(std)]
fn argv_to_command_line_0_inplace<A: AsRef<OsStr>>(exe: impl AsRef<Path>, args: impl IntoIterator<Item = A>, command_line: &mut Vec<u16>) {
const QUOTE : u16 = b'\"' as u16;
const BACKSLASH : u16 = b'\\' as u16;
command_line.clear();
command_line.push(QUOTE);
command_line.extend(exe.as_ref().as_os_str().encode_wide());
command_line.push(QUOTE);
for arg in args {
let arg = arg.as_ref();
let quote = arg.is_empty() || arg.encode_wide().any(|ch| ch == b' ' as u16 || ch == b'\t' as u16);
command_line.push(b' ' as _);
if quote { command_line.push(QUOTE) }
let mut arg = arg.encode_wide();
while let Some(ch) = arg.next() {
match ch {
BACKSLASH => {
let mut backslashes = 1;
loop {
match arg.next() {
Some(BACKSLASH) => backslashes += 1,
Some(QUOTE) => {
for _ in 0 .. backslashes {
command_line.push(BACKSLASH);
command_line.push(BACKSLASH);
}
command_line.push(BACKSLASH);
command_line.push(QUOTE);
break;
},
Some(ch) => {
for _ in 0 .. backslashes { command_line.push(BACKSLASH) }
command_line.push(ch);
break;
},
None => {
for _ in 0 .. backslashes { command_line.push(BACKSLASH) }
break;
},
}
}
},
QUOTE => {
command_line.push(BACKSLASH);
command_line.push(ch);
},
_ => command_line.push(ch),
}
}
if quote { command_line.push(QUOTE) }
}
command_line.push(0);
}
pub fn create_process_a(
application_name: impl TryIntoAsOptCStr,
command_line: Option<&[u8]>,
process_attributes: Option<&security::Attributes>,
thread_attributes: Option<&security::Attributes>,
inherit_handles: bool,
creation_flags: impl Into<process::CreationFlags>,
environment: impl TryIntoEnvironment,
current_directory: impl TryIntoAsOptCStr,
startup_info: &impl process::AsStartupInfoA,
) -> Result<process::Information, Error> {
if !command_line.as_ref().map_or(false, |c| c.ends_with(&[0])) { return Err(Error(E_STRING_NOT_NULL_TERMINATED as _)) } let creation_flags = creation_flags.into().into();
let mut process_information = Default::default();
Error::get_last_if(0 == unsafe { CreateProcessA(
application_name.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
command_line.as_ref().map_or(null(), |c| c.as_ptr()) as *mut _,
process_attributes.map_or(null(), |a| a) as *mut _,
thread_attributes.map_or(null(), |a| a) as *mut _,
inherit_handles as _,
creation_flags,
environment.as_env_ptr(creation_flags & CREATE_UNICODE_ENVIRONMENT != 0)?,
current_directory.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
startup_info.as_winapi()?,
&mut process_information
)})?;
Ok(unsafe { process::Information::from_raw(process_information) })
}
pub fn create_process_w(
application_name: impl TryIntoAsOptCStr<u16>,
mut command_line: Option<&mut [u16]>,
process_attributes: Option<&security::Attributes>,
thread_attributes: Option<&security::Attributes>,
inherit_handles: bool,
creation_flags: impl Into<process::CreationFlags>,
environment: impl TryIntoEnvironment,
current_directory: impl TryIntoAsOptCStr<u16>,
startup_info: &impl process::AsStartupInfoW,
) -> Result<process::Information, Error> {
if !command_line.as_ref().map_or(false, |c| c.ends_with(&[0])) { return Err(Error(E_STRING_NOT_NULL_TERMINATED as _)) } let creation_flags = creation_flags.into().into();
let mut process_information = Default::default();
Error::get_last_if(0 == unsafe { CreateProcessW(
application_name.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
command_line.as_mut().map_or(null_mut(), |c| c.as_mut_ptr()),
process_attributes.map_or(null(), |a| a) as *mut _,
thread_attributes.map_or(null(), |a| a) as *mut _,
inherit_handles as _,
creation_flags,
environment.as_env_ptr(creation_flags & CREATE_UNICODE_ENVIRONMENT != 0)?,
current_directory.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
startup_info.as_winapi()?,
&mut process_information
)})?;
Ok(unsafe { process::Information::from_raw(process_information) })
}
pub fn create_process_as_user_a(
token: &crate::token::OwnedHandle,
application_name: impl TryIntoAsOptCStr,
command_line: Option<&[u8]>,
process_attributes: Option<&security::Attributes>,
thread_attributes: Option<&security::Attributes>,
inherit_handles: bool,
creation_flags: impl Into<process::CreationFlags>,
environment: impl TryIntoEnvironment,
current_directory: impl TryIntoAsOptCStr,
startup_info: &impl process::AsStartupInfoA,
) -> Result<process::Information, Error> {
if !command_line.as_ref().map_or(false, |c| c.ends_with(&[0])) { return Err(Error(E_STRING_NOT_NULL_TERMINATED as _)) } let creation_flags = creation_flags.into().into();
let mut process_information = Default::default();
extern "system" { fn CreateProcessAsUserA(
hToken: HANDLE,
lpApplicationName: LPCSTR,
lpCommandLine: LPSTR,
lpProcessAttributes: LPSECURITY_ATTRIBUTES,
lpThreadAttributes: LPSECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: LPVOID,
lpCurrentDirectory: LPCSTR,
lpStartupInfo: LPSTARTUPINFOA,
lpProcessInformation: LPPROCESS_INFORMATION,
) -> BOOL;}
Error::get_last_if(0 == unsafe { CreateProcessAsUserA(
token.as_handle(),
application_name.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
command_line.as_ref().map_or(null(), |c| c.as_ptr()) as *mut _,
process_attributes.map_or(null(), |a| a) as *mut _,
thread_attributes.map_or(null(), |a| a) as *mut _,
inherit_handles as _,
creation_flags,
environment.as_env_ptr(creation_flags & CREATE_UNICODE_ENVIRONMENT != 0)?,
current_directory.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
startup_info.as_winapi()?,
&mut process_information
)})?;
Ok(unsafe { process::Information::from_raw(process_information) })
}
pub fn create_process_as_user_w(
token: &crate::token::OwnedHandle,
application_name: impl TryIntoAsOptCStr<u16>,
mut command_line: Option<&mut [u16]>,
process_attributes: Option<&security::Attributes>,
thread_attributes: Option<&security::Attributes>,
inherit_handles: bool,
creation_flags: impl Into<process::CreationFlags>,
environment: impl TryIntoEnvironment,
current_directory: impl TryIntoAsOptCStr<u16>,
startup_info: &impl process::AsStartupInfoW,
) -> Result<process::Information, Error> {
if !command_line.as_ref().map_or(false, |c| c.ends_with(&[0])) { return Err(Error(E_STRING_NOT_NULL_TERMINATED as _)) } let creation_flags = creation_flags.into().into();
let mut process_information = Default::default();
Error::get_last_if(0 == unsafe { CreateProcessAsUserW(
token.as_handle(),
application_name.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
command_line.as_mut().map_or(null_mut(), |c| c.as_mut_ptr()),
process_attributes.map_or(null(), |a| a) as *mut _,
thread_attributes.map_or(null(), |a| a) as *mut _,
inherit_handles as _,
creation_flags,
environment.as_env_ptr(creation_flags & CREATE_UNICODE_ENVIRONMENT != 0)?,
current_directory.try_into().map_err(|_| Error(E_STRING_NOT_NULL_TERMINATED as _))?.as_opt_cstr(),
startup_info.as_winapi()?,
&mut process_information
)})?;
Ok(unsafe { process::Information::from_raw(process_information) })
}
fn _create_process_with_logon_w() -> Result<process::Information, Error> { unimplemented!() }
fn _create_process_with_token_w() -> Result<process::Information, Error> { unimplemented!() }
pub fn exit_process(exit_code: u32) -> ! { unsafe { ExitProcess(exit_code); core::hint::unreachable_unchecked() } }
pub fn get_current_process() -> process::PsuedoHandle<'static> { unsafe { process::PsuedoHandle::from_raw(GetCurrentProcess()).unwrap() } }
pub fn get_current_process_id() -> process::Id { unsafe { GetCurrentProcessId() } }
pub fn get_exit_code_process<'a>(process: impl AsRef<process::Handle<'a>>) -> Result<u32, Error> {
let mut exit_code = 0;
Error::get_last_if(0 == unsafe { GetExitCodeProcess(process.as_ref().as_handle(), &mut exit_code) })?;
Ok(exit_code)
}
pub fn is_process_running<'a>(process: impl AsRef<process::Handle<'a>>) -> bool {
WAIT_TIMEOUT == unsafe { WaitForSingleObject(process.as_ref().as_handle(), 0) }
}
pub fn wait_for_process<'a>(process: impl AsRef<process::Handle<'a>>) -> Result<u32, Error> {
match unsafe { WaitForSingleObject(process.as_ref().as_handle(), INFINITE) } {
WAIT_OBJECT_0 => {},
WAIT_ABANDONED_0 => return Err(Error(ERROR_ABANDONED_WAIT_0)), WAIT_TIMEOUT => return Err(Error(ERROR_ABANDONED_WAIT_63)), WAIT_FAILED => return Err(Error::get_last()),
_ => return Err(Error(ERROR_ABANDONED_WAIT_63)), }
get_exit_code_process(process)
}
#[cfg(std)] #[test] fn test_wait_exit() {
use winapi::um::minwinbase::STILL_ACTIVE;
use std::process::*;
let child : Child = Command::new("cmd").args("/C ping localhost -n 2 && exit /B 3".split(' ')).stdout(Stdio::null()).spawn().unwrap();
let child = process::OwnedHandle::from(child);
assert!(is_process_running(&child));
assert_eq!(STILL_ACTIVE, get_exit_code_process(&child).unwrap());
assert_eq!(3, wait_for_process(&child).unwrap());
assert!(!is_process_running(&child));
assert_eq!(3, get_exit_code_process(&child).unwrap());
}