Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add GUIConsole sample * Remove acrylic native functions, add a title bar * Fix WPF app namespaces * Respond to PR feedback * Removed unused native calls, and fix up some stray spaces * Switch pwsh to powershell * Missed a spot. * Fix typo, add newlines
- Loading branch information
Showing
22 changed files
with
1,193 additions
and
0 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/GUIConsole.ConPTY.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
</Project> |
23 changes: 23 additions & 0 deletions
23
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Native/ConsoleApi.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System.Runtime.InteropServices; | ||
|
||
namespace GUIConsole.ConPTY.Native | ||
{ | ||
/// <summary> | ||
/// PInvoke signatures for Win32's Console API. | ||
/// </summary> | ||
static class ConsoleApi | ||
{ | ||
[DllImport("kernel32.dll", SetLastError = true)] | ||
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add); | ||
internal delegate bool ConsoleEventDelegate(CtrlTypes ctrlType); | ||
|
||
internal enum CtrlTypes : uint | ||
{ | ||
CTRL_C_EVENT = 0, | ||
CTRL_BREAK_EVENT, | ||
CTRL_CLOSE_EVENT, | ||
CTRL_LOGOFF_EVENT = 5, | ||
CTRL_SHUTDOWN_EVENT | ||
} | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Native/ProcessApi.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace GUIConsole.ConPTY.Native | ||
{ | ||
/// <summary> | ||
/// PInvoke signatures for Win32's Process API. | ||
/// </summary> | ||
static class ProcessApi | ||
{ | ||
internal const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000; | ||
|
||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | ||
internal struct STARTUPINFOEX | ||
{ | ||
public STARTUPINFO StartupInfo; | ||
public IntPtr lpAttributeList; | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | ||
internal struct STARTUPINFO | ||
{ | ||
public int cb; | ||
public string lpReserved; | ||
public string lpDesktop; | ||
public string lpTitle; | ||
public int dwX; | ||
public int dwY; | ||
public int dwXSize; | ||
public int dwYSize; | ||
public int dwXCountChars; | ||
public int dwYCountChars; | ||
public int dwFillAttribute; | ||
public int dwFlags; | ||
public short wShowWindow; | ||
public short cbReserved2; | ||
public IntPtr lpReserved2; | ||
public IntPtr hStdInput; | ||
public IntPtr hStdOutput; | ||
public IntPtr hStdError; | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal struct PROCESS_INFORMATION | ||
{ | ||
public IntPtr hProcess; | ||
public IntPtr hThread; | ||
public int dwProcessId; | ||
public int dwThreadId; | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal struct SECURITY_ATTRIBUTES | ||
{ | ||
public int nLength; | ||
public IntPtr lpSecurityDescriptor; | ||
public int bInheritHandle; | ||
} | ||
|
||
[DllImport("kernel32.dll", SetLastError = true)] | ||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool InitializeProcThreadAttributeList( | ||
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); | ||
|
||
[DllImport("kernel32.dll", SetLastError = true)] | ||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool UpdateProcThreadAttribute( | ||
IntPtr lpAttributeList, uint dwFlags, IntPtr attribute, IntPtr lpValue, | ||
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize); | ||
|
||
[DllImport("kernel32.dll")] | ||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool CreateProcess( | ||
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, | ||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, | ||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, | ||
out PROCESS_INFORMATION lpProcessInformation); | ||
|
||
[DllImport("kernel32.dll", SetLastError = true)] | ||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList); | ||
|
||
[DllImport("kernel32.dll", SetLastError = true)] | ||
internal static extern bool CloseHandle(IntPtr hObject); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Native/PseudoConsoleApi.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using Microsoft.Win32.SafeHandles; | ||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace GUIConsole.ConPTY.Native | ||
{ | ||
/// <summary> | ||
/// PInvoke signatures for Win32's PseudoConsole API. | ||
/// </summary> | ||
static class PseudoConsoleApi | ||
{ | ||
internal const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016; | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal struct COORD | ||
{ | ||
public short X; | ||
public short Y; | ||
} | ||
|
||
[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 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); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Processes/Process.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using static GUIConsole.ConPTY.Native.ProcessApi; | ||
|
||
namespace GUIConsole.ConPTY.Processes | ||
{ | ||
/// <summary> | ||
/// Represents an instance of a process. | ||
/// </summary> | ||
internal sealed class Process : IDisposable | ||
{ | ||
public Process(STARTUPINFOEX startupInfo, PROCESS_INFORMATION processInfo) | ||
{ | ||
StartupInfo = startupInfo; | ||
ProcessInfo = processInfo; | ||
} | ||
|
||
public STARTUPINFOEX StartupInfo { get; } | ||
public 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; | ||
} | ||
} | ||
|
||
~Process() | ||
{ | ||
Dispose(false); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
#endregion | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Processes/ProcessFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System; | ||
using System.ComponentModel; | ||
using System.Runtime.InteropServices; | ||
using static GUIConsole.ConPTY.Native.ProcessApi; | ||
|
||
namespace GUIConsole.ConPTY.Processes | ||
{ | ||
/// <summary> | ||
/// Support for starting and configuring processes. | ||
/// </summary> | ||
/// <remarks> | ||
/// Possible to replace with managed code? The key is being able to provide the PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE attribute | ||
/// </remarks> | ||
static class ProcessFactory | ||
{ | ||
/// <summary> | ||
/// Start and configure a process. The return value represents the process and should be disposed. | ||
/// </summary> | ||
internal static Process Start(string command, IntPtr attributes, IntPtr hPC) | ||
{ | ||
var startupInfo = ConfigureProcessThread(hPC, attributes); | ||
var processInfo = RunProcess(ref startupInfo, command); | ||
return new Process(startupInfo, processInfo); | ||
} | ||
|
||
private static STARTUPINFOEX ConfigureProcessThread(IntPtr hPC, IntPtr attributes) | ||
{ | ||
// this method implements the behavior described in https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#preparing-for-creation-of-the-child-process | ||
|
||
var lpSize = IntPtr.Zero; | ||
var success = InitializeProcThreadAttributeList( | ||
lpAttributeList: IntPtr.Zero, | ||
dwAttributeCount: 1, | ||
dwFlags: 0, | ||
lpSize: ref lpSize | ||
); | ||
if (success || lpSize == IntPtr.Zero) // we're not expecting `success` here, we just want to get the calculated lpSize | ||
{ | ||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not calculate the number of bytes for the attribute list."); | ||
} | ||
|
||
var startupInfo = new STARTUPINFOEX(); | ||
startupInfo.StartupInfo.cb = Marshal.SizeOf<STARTUPINFOEX>(); | ||
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize); | ||
|
||
success = InitializeProcThreadAttributeList( | ||
lpAttributeList: startupInfo.lpAttributeList, | ||
dwAttributeCount: 1, | ||
dwFlags: 0, | ||
lpSize: ref lpSize | ||
); | ||
if (!success) | ||
{ | ||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not set up attribute list."); | ||
} | ||
|
||
success = UpdateProcThreadAttribute( | ||
lpAttributeList: startupInfo.lpAttributeList, | ||
dwFlags: 0, | ||
attribute: attributes, | ||
lpValue: hPC, | ||
cbSize: (IntPtr)IntPtr.Size, | ||
lpPreviousValue: IntPtr.Zero, | ||
lpReturnSize: IntPtr.Zero | ||
); | ||
if (!success) | ||
{ | ||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not set pseudoconsole thread attribute."); | ||
} | ||
|
||
return startupInfo; | ||
} | ||
|
||
private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEX sInfoEx, string commandLine) | ||
{ | ||
int securityAttributeSize = Marshal.SizeOf<SECURITY_ATTRIBUTES>(); | ||
var pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize }; | ||
var tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize }; | ||
var success = CreateProcess( | ||
lpApplicationName: null, | ||
lpCommandLine: commandLine, | ||
lpProcessAttributes: ref pSec, | ||
lpThreadAttributes: ref tSec, | ||
bInheritHandles: false, | ||
dwCreationFlags: EXTENDED_STARTUPINFO_PRESENT, | ||
lpEnvironment: IntPtr.Zero, | ||
lpCurrentDirectory: null, | ||
lpStartupInfo: ref sInfoEx, | ||
lpProcessInformation: out PROCESS_INFORMATION pInfo | ||
); | ||
if (!success) | ||
{ | ||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not create process."); | ||
} | ||
|
||
return pInfo; | ||
} | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/PseudoConsole.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Microsoft.Win32.SafeHandles; | ||
using System; | ||
using System.ComponentModel; | ||
using static GUIConsole.ConPTY.Native.PseudoConsoleApi; | ||
|
||
namespace GUIConsole.ConPTY | ||
{ | ||
/// <summary> | ||
/// Utility functions around the new Pseudo Console APIs. | ||
/// </summary> | ||
internal sealed class PseudoConsole : IDisposable | ||
{ | ||
public static readonly IntPtr PseudoConsoleThreadAttribute = (IntPtr)PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE; | ||
|
||
public IntPtr Handle { get; } | ||
|
||
private PseudoConsole(IntPtr handle) | ||
{ | ||
this.Handle = handle; | ||
} | ||
|
||
internal static PseudoConsole Create(SafeFileHandle inputReadSide, SafeFileHandle outputWriteSide, int width, int height) | ||
{ | ||
var createResult = CreatePseudoConsole( | ||
new COORD { X = (short)width, Y = (short)height }, | ||
inputReadSide, outputWriteSide, | ||
0, out IntPtr hPC); | ||
if(createResult != 0) | ||
{ | ||
throw new Win32Exception(createResult, "Could not create pseudo console."); | ||
} | ||
return new PseudoConsole(hPC); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
ClosePseudoConsole(Handle); | ||
} | ||
} | ||
} |
Oops, something went wrong.