-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconpty.h
More file actions
159 lines (141 loc) · 6.48 KB
/
Copy pathconpty.h
File metadata and controls
159 lines (141 loc) · 6.48 KB
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include <windows.h>
#include <string>
#include <sstream>
#include <strsafe.h>
#include <memory>
#pragma once
const unsigned int PTY_SIGNAL_RESIZE_WINDOW = 8u;
HRESULT CreateConPty(const std::wstring& cmdline, // _In_
const unsigned short w, // _In_
const unsigned short h, // _In_
HANDLE* const hInput, // _Out_
HANDLE* const hOutput, // _Out_
HANDLE* const hSignal, // _Out_
PROCESS_INFORMATION* const piPty); // _Out_
bool SignalResizeWindow(const HANDLE hSignal,
const unsigned short w,
const unsigned short h);
// Function Description:
// - Creates a headless conhost in "pty mode" and launches the given commandline
// attached to the conhost. Gives back handles to three different pipes:
// * hInput: The caller can write input to the conhost, encoded in utf-8, on
// this pipe. For keys that don't have character representations, the
// caller should use the `TERM=xterm` VT sequences for encoding the input.
// * hOutput: The caller should read from this pipe. The headless conhost will
// "render" it's state to a stream of utf-8 encoded text with VT sequences.
// * hSignal: The caller can use this to resize the size of the underlying PTY
// using the SignalResizeWindow function.
// Arguments:
// - cmdline: The commandline to launch as a console process attached to the pty
// that's created.
// - w: The initial width of the pty, in characters
// - h: The initial height of the pty, in characters
// - hInput: A handle to the pipe for writing input to the pty.
// - hOutput: A handle to the pipe for reading the output of the pty.
// - hSignal: A handle to the pipe for writing signal messages to the pty.
// - piPty: The PROCESS_INFORMATION of the pty process. NOTE: This is *not* the
// PROCESS_INFORMATION of the process that's created as a result the cmdline.
// Return Value:
// - S_OK if we succeeded, or an appropriate HRESULT for failing format the
// commandline or failing to launch the conhost
__declspec(noinline) inline
HRESULT CreateConPty(const std::wstring& cmdline,
const unsigned short w,
const unsigned short h,
HANDLE* const hInput,
HANDLE* const hOutput,
HANDLE* const hSignal,
PROCESS_INFORMATION* const piPty)
{
// Create some anon pipes so we can pass handles down and into the console.
// IMPORTANT NOTE:
// We're creating the pipe here with un-inheritable handles, then marking
// the conhost sides of the pipes as inheritable. We do this because if
// the entire pipe is marked as inheritable, when we pass the handles
// to CreateProcess, at some point the entire pipe object is copied to
// the conhost process, which includes the terminal side of the pipes
// (_inPipe and _outPipe). This means that if we die, there's still
// outstanding handles to our side of the pipes, and those handles are
// in conhost, despite conhost being unable to reference those handles
// and close them.
// CRITICAL: Close our side of the handles. Otherwise you'll get the same
// problem if you close conhost, but not us (the terminal).
HANDLE outPipeConhostSide;
HANDLE inPipeConhostSide;
HANDLE signalPipeConhostSide;
SECURITY_ATTRIBUTES sa;
sa = {0};
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = nullptr;
CreatePipe(&inPipeConhostSide, hInput, &sa, 0);
CreatePipe(hOutput, &outPipeConhostSide, &sa, 0);
// Mark inheritable for signal handle when creating. It'll have the same value on the other side.
sa.bInheritHandle = TRUE;
CreatePipe(&signalPipeConhostSide, hSignal, &sa, 0);
SetHandleInformation(inPipeConhostSide, HANDLE_FLAG_INHERIT, 1);
SetHandleInformation(outPipeConhostSide, HANDLE_FLAG_INHERIT, 1);
std::wstring conhostCmdline = L"conhost.exe";
conhostCmdline += L" --headless";
std::wstringstream ss;
if (w != 0 && h != 0)
{
ss << L" --width " << (unsigned long)w;
ss << L" --height " << (unsigned long)h;
}
ss << L" --signal 0x" << std::hex << HandleToUlong(signalPipeConhostSide);
conhostCmdline += ss.str();
conhostCmdline += L" -- ";
conhostCmdline += cmdline;
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFOW);
si.hStdInput = inPipeConhostSide;
si.hStdOutput = outPipeConhostSide;
si.hStdError = outPipeConhostSide;
si.dwFlags |= STARTF_USESTDHANDLES;
std::unique_ptr<wchar_t[]> mutableCommandline = std::make_unique<wchar_t[]>(conhostCmdline.length() + 1);
if (mutableCommandline == nullptr)
{
return E_OUTOFMEMORY;
}
HRESULT hr = StringCchCopy(mutableCommandline.get(), conhostCmdline.length()+1, conhostCmdline.c_str());
if (!SUCCEEDED(hr))
{
return hr;
}
bool fSuccess = !!CreateProcessW(
nullptr,
mutableCommandline.get(),
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
true, // bInheritHandles
0, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // lpCurrentDirectory
&si, // lpStartupInfo
piPty // lpProcessInformation
);
CloseHandle(inPipeConhostSide);
CloseHandle(outPipeConhostSide);
CloseHandle(signalPipeConhostSide);
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
// Function Description:
// - Resizes the pty that's connected to hSignal.
// Arguments:
// - hSignal: A signal pipe as returned by CreateConPty.
// - w: The new width of the pty, in characters
// - h: The new height of the pty, in characters
// Return Value:
// - true if the resize succeeded, else false.
__declspec(noinline) inline
bool SignalResizeWindow(HANDLE hSignal, const unsigned short w, const unsigned short h)
{
unsigned short signalPacket[3];
signalPacket[0] = PTY_SIGNAL_RESIZE_WINDOW;
signalPacket[1] = w;
signalPacket[2] = h;
return !!WriteFile(hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
}