-
Notifications
You must be signed in to change notification settings - Fork 8.5k
/
Copy pathProcessFactory.cs
99 lines (90 loc) · 3.94 KB
/
ProcessFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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;
}
}
}