From 684f1be7d98e489bb9a31e9690f1923590c62be3 Mon Sep 17 00:00:00 2001 From: Chip Locke Date: Fri, 23 Nov 2018 05:32:01 -0800 Subject: [PATCH] actually catch process exits and allow them to be raised. --- src/ConsoleBuffer/ConsoleWrapper.cs | 38 +++++++++++++++++++++++++++++ src/ConsoleBuffer/NativeMethods.cs | 16 +++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/ConsoleBuffer/ConsoleWrapper.cs b/src/ConsoleBuffer/ConsoleWrapper.cs index 3c9ed53..31160c6 100644 --- a/src/ConsoleBuffer/ConsoleWrapper.cs +++ b/src/ConsoleBuffer/ConsoleWrapper.cs @@ -5,6 +5,7 @@ namespace ConsoleBuffer using System.IO; using System.Runtime.InteropServices; using System.Text; + using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -17,6 +18,17 @@ public sealed class ConsoleWrapper : IDisposable, INotifyPropertyChanged public short Width { get; set; } public short Height { get; set; } + /// + /// True if the process is running. + /// + public bool Running { get; private set; } + + /// + /// The exit code of the process once terminated. + /// + public uint ProcessExitCode { get; private set; } + + /// /// The handle from which we read data from the console. /// @@ -143,6 +155,32 @@ private void StartProcess() { ThrowForWin32Error(Marshal.GetLastWin32Error(), "Unable to start process."); } + + this.Running = true; + this.OnPropertyChanged("Running"); + + Task.Run(() => + { + var ret = NativeMethods.WaitForSingleObject(this.processInfo.hProcess, uint.MaxValue); + if (ret != 0) + { + // XXX: do something smarter here. + Logger.Verbose($"Wait for process termination failed: {ret}"); + return; + } + if (!NativeMethods.GetExitCodeProcess(this.processInfo.hProcess, out var exitCode)) + { + Logger.Verbose($"Failed to get process exit code, errno={Marshal.GetLastWin32Error()}"); + } + + this.ProcessExitCode = exitCode; + this.Running = false; + this.OnPropertyChanged("Running"); + + // XXX: long-term I think we should have the presentation layer do this but let's dump it here for now + var msg = Encoding.UTF8.GetBytes($"\r\n[process terminated with code {exitCode}.]"); + this.Buffer.Append(msg, msg.Length); + }); } private void ReadConsoleTask() diff --git a/src/ConsoleBuffer/NativeMethods.cs b/src/ConsoleBuffer/NativeMethods.cs index fa79f07..4da1df8 100644 --- a/src/ConsoleBuffer/NativeMethods.cs +++ b/src/ConsoleBuffer/NativeMethods.cs @@ -1,4 +1,4 @@ -namespace ConsoleBuffer +namespace ConsoleBuffer { using Microsoft.Win32.SafeHandles; using System; @@ -97,7 +97,15 @@ internal static extern bool CreateProcess(string lpApplicationName, string lpCom [DllImport("kernel32.dll", SetLastError = true)] internal static extern int ClosePseudoConsole(IntPtr hPC); - + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, IntPtr lpPipeAttributes, int nSize); } -} \ No newline at end of file + internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, IntPtr lpPipeAttributes, int nSize); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode); + } +}