#![allow(non_snake_case)] pub(crate) use _winapi::make_module; #[pymodule] mod _winapi { use crate::{ builtins::PyStrRef, common::windows::ToWideString, convert::ToPyException, function::{ArgMapping, ArgSequence, OptionalArg}, stdlib::os::errno_err, PyObjectRef, PyResult, TryFromObject, VirtualMachine, }; use std::ptr::{null, null_mut}; use winapi::shared::winerror; use winapi::um::{ fileapi, handleapi, namedpipeapi, processenv, processthreadsapi, synchapi, winbase, winnt::HANDLE, }; use windows::{ core::PCWSTR, Win32::Foundation::{HINSTANCE, MAX_PATH}, Win32::System::LibraryLoader::{GetModuleFileNameW, LoadLibraryW}, }; #[pyattr] use winapi::{ shared::winerror::{ ERROR_ALREADY_EXISTS, ERROR_BROKEN_PIPE, ERROR_IO_PENDING, ERROR_MORE_DATA, ERROR_NETNAME_DELETED, ERROR_NO_DATA, ERROR_NO_SYSTEM_RESOURCES, ERROR_OPERATION_ABORTED, ERROR_PIPE_BUSY, ERROR_PIPE_CONNECTED, ERROR_SEM_TIMEOUT, WAIT_TIMEOUT, }, um::{ fileapi::OPEN_EXISTING, memoryapi::{ FILE_MAP_ALL_ACCESS, FILE_MAP_COPY, FILE_MAP_EXECUTE, FILE_MAP_READ, FILE_MAP_WRITE, }, minwinbase::STILL_ACTIVE, winbase::{ ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, CREATE_BREAKAWAY_FROM_JOB, CREATE_DEFAULT_ERROR_MODE, CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW, DETACHED_PROCESS, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE, FILE_TYPE_REMOTE, FILE_TYPE_UNKNOWN, HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, INFINITE, NORMAL_PRIORITY_CLASS, PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT, REALTIME_PRIORITY_CLASS, STARTF_USESHOWWINDOW, STARTF_USESTDHANDLES, STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, WAIT_ABANDONED, WAIT_ABANDONED_0, WAIT_OBJECT_0, }, winnt::{ DUPLICATE_CLOSE_SOURCE, DUPLICATE_SAME_ACCESS, FILE_GENERIC_READ, FILE_GENERIC_WRITE, GENERIC_READ, GENERIC_WRITE, LOCALE_NAME_MAX_LENGTH, MEM_COMMIT, MEM_FREE, MEM_IMAGE, MEM_MAPPED, MEM_PRIVATE, MEM_RESERVE, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_GUARD, PAGE_NOACCESS, PAGE_NOCACHE, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOMBINE, PAGE_WRITECOPY, PROCESS_DUP_HANDLE, SEC_COMMIT, SEC_IMAGE, SEC_LARGE_PAGES, SEC_NOCACHE, SEC_RESERVE, SEC_WRITECOMBINE, SYNCHRONIZE, }, winuser::SW_HIDE, }, }; fn GetLastError() -> u32 { unsafe { winapi::um::errhandlingapi::GetLastError() } } fn husize(h: HANDLE) -> usize { h as usize } trait Convertible { fn is_err(&self) -> bool; } impl Convertible for HANDLE { fn is_err(&self) -> bool { *self == handleapi::INVALID_HANDLE_VALUE } } impl Convertible for i32 { fn is_err(&self) -> bool { *self == 0 } } fn cvt(vm: &VirtualMachine, res: T) -> PyResult { if res.is_err() { Err(errno_err(vm)) } else { Ok(res) } } #[pyfunction] fn CloseHandle(handle: usize, vm: &VirtualMachine) -> PyResult<()> { cvt(vm, unsafe { handleapi::CloseHandle(handle as HANDLE) }).map(drop) } #[pyfunction] fn GetStdHandle(std_handle: u32, vm: &VirtualMachine) -> PyResult { cvt(vm, unsafe { processenv::GetStdHandle(std_handle) }).map(husize) } #[pyfunction] fn CreatePipe( _pipe_attrs: PyObjectRef, size: u32, vm: &VirtualMachine, ) -> PyResult<(usize, usize)> { let mut read = null_mut(); let mut write = null_mut(); cvt(vm, unsafe { namedpipeapi::CreatePipe(&mut read, &mut write, null_mut(), size) })?; Ok((read as usize, write as usize)) } #[pyfunction] fn DuplicateHandle( (src_process, src): (usize, usize), target_process: usize, access: u32, inherit: i32, options: OptionalArg, vm: &VirtualMachine, ) -> PyResult { let mut target = null_mut(); cvt(vm, unsafe { handleapi::DuplicateHandle( src_process as _, src as _, target_process as _, &mut target, access, inherit, options.unwrap_or(0), ) })?; Ok(target as usize) } #[pyfunction] fn GetCurrentProcess() -> usize { unsafe { processthreadsapi::GetCurrentProcess() as usize } } #[pyfunction] fn GetFileType(h: usize, vm: &VirtualMachine) -> PyResult { let ret = unsafe { fileapi::GetFileType(h as _) }; if ret == 0 && GetLastError() != 0 { Err(errno_err(vm)) } else { Ok(ret) } } #[derive(FromArgs)] struct CreateProcessArgs { #[pyarg(positional)] name: Option, #[pyarg(positional)] command_line: Option, #[pyarg(positional)] _proc_attrs: PyObjectRef, #[pyarg(positional)] _thread_attrs: PyObjectRef, #[pyarg(positional)] inherit_handles: i32, #[pyarg(positional)] creation_flags: u32, #[pyarg(positional)] env_mapping: Option, #[pyarg(positional)] current_dir: Option, #[pyarg(positional)] startup_info: PyObjectRef, } #[pyfunction] fn CreateProcess( args: CreateProcessArgs, vm: &VirtualMachine, ) -> PyResult<(usize, usize, u32, u32)> { let mut si = winbase::STARTUPINFOEXW::default(); si.StartupInfo.cb = std::mem::size_of_val(&si) as _; macro_rules! si_attr { ($attr:ident, $t:ty) => {{ si.StartupInfo.$attr = >::try_from_object( vm, args.startup_info.get_attr(stringify!($attr), vm)?, )? .unwrap_or(0) as _ }}; ($attr:ident) => {{ si.StartupInfo.$attr = >::try_from_object( vm, args.startup_info.get_attr(stringify!($attr), vm)?, )? .unwrap_or(0) }}; } si_attr!(dwFlags); si_attr!(wShowWindow); si_attr!(hStdInput, usize); si_attr!(hStdOutput, usize); si_attr!(hStdError, usize); let mut env = args .env_mapping .map(|m| getenvironment(m, vm)) .transpose()?; let env = env.as_mut().map_or_else(null_mut, |v| v.as_mut_ptr()); let mut attrlist = getattributelist(args.startup_info.get_attr("lpAttributeList", vm)?, vm)?; si.lpAttributeList = attrlist .as_mut() .map_or_else(null_mut, |l| l.attrlist.as_mut_ptr() as _); let wstr = |s: PyStrRef| { let ws = widestring::WideCString::from_str(s.as_str()) .map_err(|err| err.to_pyexception(vm))?; Ok(ws.into_vec_with_nul()) }; let app_name = args.name.map(wstr).transpose()?; let app_name = app_name.as_ref().map_or_else(null, |w| w.as_ptr()); let mut command_line = args.command_line.map(wstr).transpose()?; let command_line = command_line .as_mut() .map_or_else(null_mut, |w| w.as_mut_ptr()); let mut current_dir = args.current_dir.map(wstr).transpose()?; let current_dir = current_dir .as_mut() .map_or_else(null_mut, |w| w.as_mut_ptr()); let procinfo = unsafe { let mut procinfo = std::mem::MaybeUninit::uninit(); let ret = processthreadsapi::CreateProcessW( app_name, command_line, null_mut(), null_mut(), args.inherit_handles, args.creation_flags | winbase::EXTENDED_STARTUPINFO_PRESENT | winbase::CREATE_UNICODE_ENVIRONMENT, env as _, current_dir, &mut si as *mut winbase::STARTUPINFOEXW as _, procinfo.as_mut_ptr(), ); if ret == 0 { return Err(errno_err(vm)); } procinfo.assume_init() }; Ok(( procinfo.hProcess as usize, procinfo.hThread as usize, procinfo.dwProcessId, procinfo.dwThreadId, )) } fn getenvironment(env: ArgMapping, vm: &VirtualMachine) -> PyResult> { let keys = env.mapping().keys(vm)?; let values = env.mapping().values(vm)?; let keys = ArgSequence::try_from_object(vm, keys)?.into_vec(); let values = ArgSequence::try_from_object(vm, values)?.into_vec(); if keys.len() != values.len() { return Err( vm.new_runtime_error("environment changed size during iteration".to_owned()) ); } let mut out = widestring::WideString::new(); for (k, v) in keys.into_iter().zip(values.into_iter()) { let k = PyStrRef::try_from_object(vm, k)?; let k = k.as_str(); let v = PyStrRef::try_from_object(vm, v)?; let v = v.as_str(); if k.contains('\0') || v.contains('\0') { return Err(crate::exceptions::cstring_error(vm)); } if k.is_empty() || k[1..].contains('=') { return Err(vm.new_value_error("illegal environment variable name".to_owned())); } out.push_str(k); out.push_str("="); out.push_str(v); out.push_str("\0"); } out.push_str("\0"); Ok(out.into_vec()) } struct AttrList { handlelist: Option>, attrlist: Vec, } impl Drop for AttrList { fn drop(&mut self) { unsafe { processthreadsapi::DeleteProcThreadAttributeList(self.attrlist.as_mut_ptr() as _) }; } } fn getattributelist(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult> { >::try_from_object(vm, obj)? .map(|mapping| { let handlelist = mapping .as_ref() .get_item("handle_list", vm) .ok() .and_then(|obj| { >>::try_from_object(vm, obj) .map(|s| match s { Some(s) if !s.is_empty() => Some(s.into_vec()), _ => None, }) .transpose() }) .transpose()?; let attr_count = handlelist.is_some() as u32; let mut size = 0; let ret = unsafe { processthreadsapi::InitializeProcThreadAttributeList( null_mut(), attr_count, 0, &mut size, ) }; if ret != 0 || GetLastError() != winerror::ERROR_INSUFFICIENT_BUFFER { return Err(errno_err(vm)); } let mut attrlist = vec![0u8; size]; let ret = unsafe { processthreadsapi::InitializeProcThreadAttributeList( attrlist.as_mut_ptr() as _, attr_count, 0, &mut size, ) }; if ret == 0 { return Err(errno_err(vm)); } let mut attrs = AttrList { handlelist, attrlist, }; if let Some(ref mut handlelist) = attrs.handlelist { let ret = unsafe { processthreadsapi::UpdateProcThreadAttribute( attrs.attrlist.as_mut_ptr() as _, 0, (2 & 0xffff) | 0x20000, // PROC_THREAD_ATTRIBUTE_HANDLE_LIST handlelist.as_mut_ptr() as _, (handlelist.len() * std::mem::size_of::()) as _, null_mut(), null_mut(), ) }; if ret == 0 { return Err(errno_err(vm)); } } Ok(attrs) }) .transpose() } #[pyfunction] fn WaitForSingleObject(h: usize, ms: u32, vm: &VirtualMachine) -> PyResult { let ret = unsafe { synchapi::WaitForSingleObject(h as _, ms) }; if ret == winbase::WAIT_FAILED { Err(errno_err(vm)) } else { Ok(ret) } } #[pyfunction] fn GetExitCodeProcess(h: usize, vm: &VirtualMachine) -> PyResult { let mut ec = 0; cvt(vm, unsafe { processthreadsapi::GetExitCodeProcess(h as _, &mut ec) })?; Ok(ec) } #[pyfunction] fn TerminateProcess(h: usize, exit_code: u32, vm: &VirtualMachine) -> PyResult<()> { cvt(vm, unsafe { processthreadsapi::TerminateProcess(h as _, exit_code) }) .map(drop) } // TODO: ctypes.LibraryLoader.LoadLibrary #[allow(dead_code)] fn LoadLibrary(path: PyStrRef, vm: &VirtualMachine) -> PyResult { let path = path.as_str().to_wides_with_nul(); let handle = unsafe { LoadLibraryW(PCWSTR::from_raw(path.as_ptr())).unwrap() }; if handle.is_invalid() { return Err(vm.new_runtime_error("LoadLibrary failed".to_owned())); } Ok(handle.0) } #[pyfunction] fn GetModuleFileName(handle: isize, vm: &VirtualMachine) -> PyResult { let mut path: Vec = vec![0; MAX_PATH as usize]; let handle = HINSTANCE(handle); let length = unsafe { GetModuleFileNameW(handle, &mut path) }; if length == 0 { return Err(vm.new_runtime_error("GetModuleFileName failed".to_owned())); } let (path, _) = path.split_at(length as usize); Ok(String::from_utf16(path).unwrap()) } }