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
This commit is contained in:
Will Fuqua 2018-09-21 07:29:09 +07:00
parent 637c57473e
commit bf32b8d48f
No known key found for this signature in database
GPG Key ID: 67B6F489167C16A3
4 changed files with 71 additions and 32 deletions

View File

@ -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);

View File

@ -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
/// <summary>
/// Start and configure a process. The return value should be considered opaque, and eventually disposed.
/// </summary>
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
}
}
}

View File

@ -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
}
}

View File

@ -26,14 +26,14 @@ namespace MiniTerm
/// </summary>
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));