1use std::ffi::CString;
2
3use windows::{
4 core::{PCSTR, PSTR},
5 Win32::{
6 Foundation::CloseHandle,
7 System::Threading::{
8 CreateProcessA, ResumeThread, TerminateProcess, WaitForSingleObject, CREATE_SUSPENDED,
9 INFINITE, PROCESS_INFORMATION, STARTUPINFOA,
10 },
11 },
12};
13
14pub struct CommandLineBuilder {
15 args: Vec<String>,
16}
17
18impl CommandLineBuilder {
19 pub fn new() -> Self {
20 Self { args: Vec::new() }
21 }
22
23 pub fn arg(mut self, arg: &str) -> Self {
24 self.args.push(arg.to_string());
25 self
26 }
27
28 pub fn encode(&self) -> String {
29 let mut params = String::new();
30 for arg in self.args.iter() {
31 params.push(' ');
32 if arg.len() == 0 {
33 params.push_str("\"\"");
34 } else if arg.find(&[' ', '\t', '"'][..]).is_none() {
35 params.push_str(&arg);
36 } else {
37 params.push('"');
38 for c in arg.chars() {
39 match c {
40 '\\' => params.push_str("\\\\"),
41 '"' => params.push_str("\\\""),
42 c => params.push(c),
43 }
44 }
45 params.push('"');
46 }
47 }
48 if !params.is_empty() {
49 params.remove(0);
50 }
51 params
52 }
53}
54
55pub enum ProcessControlFlow {
56 ResumeMainThread,
57 Terminate,
58}
59
60pub fn create_process(args: &[&str], work: impl Fn(u32) -> ProcessControlFlow) {
61 unsafe {
62 let mut si = STARTUPINFOA::default();
63 let mut pi = PROCESS_INFORMATION::default();
64 si.cb = std::mem::size_of_val(&si) as _;
65 let exe = std::env::current_exe()
66 .unwrap()
67 .to_str()
68 .unwrap()
69 .to_string();
70 let mut builder = CommandLineBuilder::new().arg(&exe);
71 for i in args {
72 builder = builder.arg(i);
73 }
74 let cmd = CString::new(builder.encode()).unwrap();
75 CreateProcessA(
76 PCSTR::null(),
77 PSTR::from_raw(cmd.as_ptr() as _),
78 None,
79 None,
80 true,
81 CREATE_SUSPENDED,
82 None,
83 PCSTR::null(),
84 &si,
85 &mut pi,
86 )
87 .unwrap();
88
89 match work(pi.dwProcessId) {
90 ProcessControlFlow::ResumeMainThread => {
91 ResumeThread(pi.hThread);
92 WaitForSingleObject(pi.hProcess, INFINITE);
93 }
94 ProcessControlFlow::Terminate => {
95 let _ = TerminateProcess(pi.hProcess, u32::MAX);
96 }
97 }
98
99 CloseHandle(pi.hThread).unwrap();
100 CloseHandle(pi.hProcess).unwrap();
101 };
102}