use std::ffi::OsString;
use std::path::Path;
use std::process::Command;
#[cfg(windows)]
pub fn forward(gui_exe: &str) -> ! {
let exe = match std::env::current_exe() {
Ok(me) => me.with_file_name(gui_exe),
Err(_) => Path::new(gui_exe).to_path_buf(),
};
std::process::exit(forward_to(&exe, std::env::args_os().skip(1)))
}
pub fn forward_to(exe: &Path, args: impl IntoIterator<Item = OsString>) -> i32 {
let mut child = match Command::new(exe).args(args).spawn() {
Ok(child) => child,
Err(e) => {
eprintln!("cannot start {}: {e}", exe.display());
return 1;
}
};
#[cfg(windows)]
let _job = job::kill_on_close(&child);
match child.wait() {
Ok(status) => status.code().unwrap_or(1),
Err(e) => {
eprintln!("cannot wait for {}: {e}", exe.display());
1
}
}
}
#[cfg(windows)]
mod job {
use std::os::windows::io::AsRawHandle;
use std::process::Child;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JobObjectExtendedLimitInformation, SetInformationJobObject,
};
pub struct Job(HANDLE);
impl Drop for Job {
fn drop(&mut self) {
let _ = unsafe { CloseHandle(self.0) };
}
}
pub fn kill_on_close(child: &Child) -> Option<Job> {
match try_kill_on_close(child) {
Ok(job) => Some(job),
Err(e) => {
eprintln!("the console shim runs without a job object: {e}");
None
}
}
}
fn try_kill_on_close(child: &Child) -> windows::core::Result<Job> {
unsafe {
let job = Job(CreateJobObjectW(None, None)?);
let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
info.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
SetInformationJobObject(
job.0,
JobObjectExtendedLimitInformation,
&info as *const _ as *const _,
std::mem::size_of_val(&info) as u32,
)?;
AssignProcessToJobObject(job.0, HANDLE(child.as_raw_handle()))?;
Ok(job)
}
}
}