I have been following the microsoft guide on creating a pseudo console session. But I am a bit uncertain how I actualy write to the console? You can see what I attempted below.
use std::{ffi::OsStr, os::windows::ffi::OsStrExt};
use windows::{
core::{Error, PWSTR},
Win32::{
Foundation::{CloseHandle, HANDLE},
Storage::FileSystem::WriteFile,
System::{
Console::{ClosePseudoConsole, CreatePseudoConsole, COORD, HPCON},
Memory::{GetProcessHeap, HeapAlloc},
Pipes::CreatePipe,
Threading::{
CreateProcessW, InitializeProcThreadAttributeList, UpdateProcThreadAttribute,
EXTENDED_STARTUPINFO_PRESENT, LPPROC_THREAD_ATTRIBUTE_LIST, PROCESS_INFORMATION,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, STARTUPINFOEXW,
},
},
},
};
pub struct PTY {
input_read_side: HANDLE,
input_write_side: HANDLE,
output_read_side: HANDLE,
output_write_side: HANDLE,
pi: PROCESS_INFORMATION,
hpcon: HPCON,
}
impl PTY {
pub unsafe fn new(size: COORD, cmd_line: &OsStr) -> Result<Self, Error> {
let (mut input_read_side, mut input_write_side) = (HANDLE::default(), HANDLE::default());
CreatePipe(&mut input_read_side, &mut input_write_side, None, 0)?;
let (mut output_read_side, mut output_write_side) = (HANDLE::default(), HANDLE::default());
CreatePipe(&mut output_read_side, &mut output_write_side, None, 0)?;
let hpcon = CreatePseudoConsole(size, input_read_side, output_write_side, 0)?;
let mut startup_info_ex = std::mem::zeroed::<STARTUPINFOEXW>();
startup_info_ex.StartupInfo.cb = std::mem::size_of::<STARTUPINFOEXW>() as u32;
let mut bytes_required: usize = 0;
// This is expected to fail
let _ = InitializeProcThreadAttributeList(
LPPROC_THREAD_ATTRIBUTE_LIST::default(),
1,
0,
&mut bytes_required,
);
let hheap = GetProcessHeap()?;
startup_info_ex.lpAttributeList = LPPROC_THREAD_ATTRIBUTE_LIST(HeapAlloc(
hheap,
windows::Win32::System::Memory::HEAP_FLAGS(0),
bytes_required,
));
InitializeProcThreadAttributeList(
startup_info_ex.lpAttributeList,
1,
0,
&mut bytes_required,
)?;
UpdateProcThreadAttribute(
startup_info_ex.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE as usize,
Some((&hpcon as *const _) as *const _),
std::mem::size_of::<HPCON>(),
None,
None,
)?;
let mut cmd_line = cmd_line.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut pi = std::mem::zeroed::<PROCESS_INFORMATION>();
CreateProcessW(
None,
PWSTR(cmd_line.as_mut_ptr()),
None,
None,
false,
EXTENDED_STARTUPINFO_PRESENT,
None,
None,
&startup_info_ex.StartupInfo,
&mut pi,
)?;
let buffer_to_write = "hi".as_bytes();
WriteFile(input_write_side, Some(buffer_to_write), None, None)?; // Doesnt do anything?
Ok(Self {
input_read_side,
input_write_side,
output_read_side,
output_write_side,
pi,
hpcon,
})
}
}
impl Drop for PTY {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.input_read_side);
let _ = CloseHandle(self.input_write_side);
let _ = CloseHandle(self.output_read_side);
let _ = CloseHandle(self.output_write_side);
let _ = CloseHandle(self.pi.hProcess);
let _ = CloseHandle(self.pi.hThread);
ClosePseudoConsole(self.hpcon);
}
}
}
WriteFile doesn’t fail. It simply does nothing. A powershell program appears when I run the code, but that is basically it.
Here is how I use the struct:
use std::ffi::OsStr;
use windows::{core::Error, Win32::System::Console::COORD};
mod pty;
fn main() -> Result<(), Error> {
let cmd_line = OsStr::new("C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe");
unsafe {
pty::PTY::new(COORD { X: 80, Y: 40 }, cmd_line)?;
}
Ok(())
}