using Microsoft.Win32.SafeHandles; using System; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MiniTerm.Native; namespace MiniTerm { /// /// The UI of the terminal. It's just a normal console window, but we're managing the input/output. /// In a "real" project this could be some other UI. /// public sealed class Terminal : IDisposable { private PseudoConsolePipe inputPipe; private PseudoConsolePipe outputPipe; private PseudoConsole pseudoConsole; private Process process; private FileStream writer; private FileStream reader; public Terminal(string command, int windowWidth, int windowHeight) { inputPipe = new PseudoConsolePipe(); outputPipe = new PseudoConsolePipe(); pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, windowWidth, windowHeight); process = ProcessFactory.Start(command, PseudoConsole.PseudoConsoleThreadAttribute, pseudoConsole.Handle); writer = new FileStream(inputPipe.WriteSide, FileAccess.Write); reader = new FileStream(outputPipe.ReadSide, FileAccess.Read); } public event EventHandler DataReceived; public event EventHandler CloseReceived; /// /// Start the psuedoconsole and run the process as shown in /// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole /// public void Run() { // copy all pseudoconsole output to stdout Task.Run(() => { var proc = System.Diagnostics.Process.GetProcessById(process.ProcessInfo.dwProcessId); var buf = new byte[1024]; while (!proc.HasExited) { var length = reader.Read(buf, 0, buf.Length); if (length == 0) break; DataReceived?.Invoke(this, buf.Take(length).ToArray()); } CloseReceived?.Invoke(this, 0); }); } public void OnInput(byte[] data) { writer.Write(data, 0, data.Length); writer.Flush(); } public void OnClose() { writer.WriteByte(0x03); writer.Flush(); } private void DisposeResources(params IDisposable[] disposables) { foreach (var disposable in disposables) { disposable.Dispose(); } } public void Dispose() { DisposeResources(reader, writer, process, pseudoConsole, outputPipe, inputPipe); } public void ChangeWindowSize(short width, short height) { PseudoConsoleApi.ResizePseudoConsole(pseudoConsole.Handle, new PseudoConsoleApi.COORD { X = width, Y = height }); } } }