From bf32b8d48f16b238aa5cb2044efc4b612f1ade2d Mon Sep 17 00:00:00 2001 From: Will Fuqua Date: Fri, 21 Sep 2018 07:29:09 +0700 Subject: [PATCH] implement dispose pattern - Full Dispose Pattern for ProcessResources since it has unmanaged resources - Basic Dispose Pattern for PseudoConsolePipe since it has managed resources - Fix naming of iStdOut to hStdOut - Change parameter order of Process.Start to make more sense --- .../MiniTerm/Native/PseudoConsoleApi.cs | 3 - samples/ConPTY/MiniTerm/MiniTerm/Process.cs | 73 +++++++++++++------ .../MiniTerm/MiniTerm/PseudoConsolePipe.cs | 17 ++++- samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs | 10 +-- 4 files changed, 71 insertions(+), 32 deletions(-) diff --git a/samples/ConPTY/MiniTerm/MiniTerm/Native/PseudoConsoleApi.cs b/samples/ConPTY/MiniTerm/MiniTerm/Native/PseudoConsoleApi.cs index 71e26ee79d..8ad317550c 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/Native/PseudoConsoleApi.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/Native/PseudoConsoleApi.cs @@ -21,9 +21,6 @@ namespace MiniTerm.Native [DllImport("kernel32.dll", SetLastError = true)] internal static extern int CreatePseudoConsole(COORD size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC); - [DllImport("kernel32.dll", SetLastError = true)] - internal static extern int CreatePseudoConsole(COORD size, IntPtr hInput, IntPtr hOutput, uint dwFlags, out IntPtr phPC); - [DllImport("kernel32.dll", SetLastError = true)] internal static extern int ResizePseudoConsole(IntPtr hPC, COORD size); diff --git a/samples/ConPTY/MiniTerm/MiniTerm/Process.cs b/samples/ConPTY/MiniTerm/MiniTerm/Process.cs index be79b5fb3e..3756077e2a 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/Process.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/Process.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using static MiniTerm.Native.ProcessApi; +using static MiniTerm.Native.PseudoConsoleApi; namespace MiniTerm { @@ -15,7 +16,7 @@ namespace MiniTerm /// /// Start and configure a process. The return value should be considered opaque, and eventually disposed. /// - internal static ProcessResources Start(IntPtr hPC, string command, IntPtr attributes) + internal static ProcessResources Start(string command, IntPtr attributes, IntPtr hPC) { var startupInfo = ConfigureProcessThread(hPC, attributes); var processInfo = RunProcess(ref startupInfo, "cmd.exe"); @@ -93,26 +94,6 @@ namespace MiniTerm return pInfo; } - private static void CleanUp(STARTUPINFOEX startupInfo, PROCESS_INFORMATION processInfo) - { - // Free the attribute list - if (startupInfo.lpAttributeList != IntPtr.Zero) - { - DeleteProcThreadAttributeList(startupInfo.lpAttributeList); - Marshal.FreeHGlobal(startupInfo.lpAttributeList); - } - - // Close process and thread handles - if (processInfo.hProcess != IntPtr.Zero) - { - CloseHandle(processInfo.hProcess); - } - if (processInfo.hThread != IntPtr.Zero) - { - CloseHandle(processInfo.hThread); - } - } - internal sealed class ProcessResources : IDisposable { public ProcessResources(STARTUPINFOEX startupInfo, PROCESS_INFORMATION processInfo) @@ -124,10 +105,58 @@ namespace MiniTerm STARTUPINFOEX StartupInfo { get; } PROCESS_INFORMATION ProcessInfo { get; } + #region IDisposable Support + + private bool disposedValue = false; // To detect redundant calls + + void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // dispose managed state (managed objects). + } + + // dispose unmanaged state + + // Free the attribute list + if (StartupInfo.lpAttributeList != IntPtr.Zero) + { + DeleteProcThreadAttributeList(StartupInfo.lpAttributeList); + Marshal.FreeHGlobal(StartupInfo.lpAttributeList); + } + + // Close process and thread handles + if (ProcessInfo.hProcess != IntPtr.Zero) + { + CloseHandle(ProcessInfo.hProcess); + } + if (ProcessInfo.hThread != IntPtr.Zero) + { + CloseHandle(ProcessInfo.hThread); + } + + disposedValue = true; + } + } + + ~ProcessResources() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + // This code added to correctly implement the disposable pattern. public void Dispose() { - CleanUp(StartupInfo, ProcessInfo); + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // use the following line if the finalizer is overridden above. + GC.SuppressFinalize(this); } + + #endregion } } } diff --git a/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsolePipe.cs b/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsolePipe.cs index d2e36d6f0c..604afb2d17 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsolePipe.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsolePipe.cs @@ -24,10 +24,23 @@ namespace MiniTerm } } + #region IDisposable + + void Dispose(bool disposing) + { + if (disposing) + { + ReadSide?.Dispose(); + WriteSide?.Dispose(); + } + } + public void Dispose() { - ReadSide.Dispose(); - WriteSide.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + + #endregion } } diff --git a/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs b/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs index 546355c542..8de735628b 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs @@ -26,14 +26,14 @@ namespace MiniTerm /// private static void EnableVirtualTerminalSequenceProcessing() { - var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (!GetConsoleMode(iStdOut, out uint outConsoleMode)) + var hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (!GetConsoleMode(hStdOut, out uint outConsoleMode)) { throw new InvalidOperationException("Could not get console mode"); } outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode(iStdOut, outConsoleMode)) + if (!SetConsoleMode(hStdOut, outConsoleMode)) { throw new InvalidOperationException("Could not enable virtual terminal processing"); } @@ -48,8 +48,8 @@ namespace MiniTerm { using (var inputPipe = new PseudoConsolePipe()) using (var outputPipe = new PseudoConsolePipe()) - using (var pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, Console.WindowWidth, Console.WindowHeight)) - using (var process = Process.Start(pseudoConsole.Handle, command, PseudoConsole.PseudoConsoleThreadAttribute)) + using (var pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, (short)Console.WindowWidth, (short)Console.WindowHeight)) + using (var process = Process.Start(command, PseudoConsole.PseudoConsoleThreadAttribute, pseudoConsole.Handle)) { // set up a background task to copy all pseudoconsole output to stdout Task.Run(() => CopyPipeToOutput(outputPipe.ReadSide));