diff --git a/OpenConsole.sln b/OpenConsole.sln
index 35fa999fd19..2824b840799 100644
--- a/OpenConsole.sln
+++ b/OpenConsole.sln
@@ -248,6 +248,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "s
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty", "src\winconpty\winconpty.vcxproj", "{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|ARM64 = AuditMode|ARM64
@@ -1029,6 +1031,21 @@ Global
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.Build.0 = Release|x64
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.ActiveCfg = Release|Win32
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.Build.0 = Release|Win32
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|ARM64.ActiveCfg = Release|ARM64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x64.ActiveCfg = Release|x64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x86.ActiveCfg = Release|Win32
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.Build.0 = Debug|ARM64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.ActiveCfg = Debug|x64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.Build.0 = Debug|x64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.ActiveCfg = Debug|Win32
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.Build.0 = Debug|Win32
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.ActiveCfg = Release|ARM64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.Build.0 = Release|ARM64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.ActiveCfg = Release|x64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.Build.0 = Release|x64
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.ActiveCfg = Release|Win32
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1090,6 +1107,7 @@ Global
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {59840756-302F-44DF-AA47-441A9D673202}
+ {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
diff --git a/consolegit2gitfilters.json b/consolegit2gitfilters.json
index 6466ad537b8..d779dec7ca3 100644
--- a/consolegit2gitfilters.json
+++ b/consolegit2gitfilters.json
@@ -14,6 +14,7 @@
"/.vs/",
"/build/",
"/src/cascadia/",
+ "/src/winconpty/",
"/.nuget/",
"/.github/",
"/samples/"
diff --git a/src/host/ft_host/Host.FeatureTests.vcxproj b/src/host/ft_host/Host.FeatureTests.vcxproj
index 7a8205072a8..ffebea0acd1 100644
--- a/src/host/ft_host/Host.FeatureTests.vcxproj
+++ b/src/host/ft_host/Host.FeatureTests.vcxproj
@@ -36,6 +36,9 @@
{18d09a24-8240-42d6-8cb6-236eee820263}
+
+ {58a03bb2-df5a-4b66-91a0-7ef3ba01269a}
+
@@ -56,4 +59,10 @@
-
\ No newline at end of file
+
+
+
+ $(OutDir)\conpty.lib;%(AdditionalDependencies)
+
+
+
diff --git a/src/winconpty/device.h b/src/winconpty/device.h
new file mode 100644
index 00000000000..61387ab7b3f
--- /dev/null
+++ b/src/winconpty/device.h
@@ -0,0 +1,27 @@
+/*++
+Copyright (c) Microsoft Corporation
+Licensed under the MIT license.
+
+Module Name:
+- device.h
+
+Abstract:
+- This header exists to reduce the differences in winconpty
+ from the in-box windows source.
+- Relies on components from Server to reach into ntdll for NtOpenFile
+ to get at the NT namespace, which is required to open the console device.
+--*/
+
+#pragma once
+
+#include "../server/DeviceHandle.h"
+
+[[nodiscard]] static inline NTSTATUS CreateClientHandle(PHANDLE Handle, HANDLE ServerHandle, PCWSTR Name, BOOLEAN Inheritable)
+{
+ return DeviceHandle::CreateClientHandle(Handle, ServerHandle, Name, Inheritable);
+}
+
+[[nodiscard]] static inline NTSTATUS CreateServerHandle(PHANDLE Handle, BOOLEAN Inheritable)
+{
+ return DeviceHandle::CreateServerHandle(Handle, Inheritable);
+}
diff --git a/src/winconpty/precomp.cpp b/src/winconpty/precomp.cpp
new file mode 100644
index 00000000000..c51e9b31b2f
--- /dev/null
+++ b/src/winconpty/precomp.cpp
@@ -0,0 +1,4 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "precomp.h"
diff --git a/src/winconpty/precomp.h b/src/winconpty/precomp.h
new file mode 100644
index 00000000000..6bf892eacb4
--- /dev/null
+++ b/src/winconpty/precomp.h
@@ -0,0 +1,49 @@
+/*++
+Copyright (c) Microsoft Corporation
+Licensed under the MIT license.
+
+Module Name:
+- precomp.h
+
+Abstract:
+- Contains external headers to include in the precompile phase of console build process.
+- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
+--*/
+
+#pragma once
+
+// Ignore checked iterators warning from VC compiler.
+#define _SCL_SECURE_NO_WARNINGS
+
+// Block minwindef.h min/max macros to prevent conflict
+#define NOMINMAX
+
+// Define and then undefine WIN32_NO_STATUS because windows.h has no guard to prevent it from double defing certain statuses
+// when included with ntstatus.h
+#define WIN32_NO_STATUS
+#include
+#undef WIN32_NO_STATUS
+
+// From ntdef.h, but that can't be included or it'll fight over PROBE_ALIGNMENT and other such arch specific defs
+typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
+/*lint -save -e624 */ // Don't complain about different typedefs.
+typedef NTSTATUS* PNTSTATUS;
+/*lint -restore */ // Resume checking for different typedefs.
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+
+// End From ntdef.h
+
+#define INLINE_NTSTATUS_FROM_WIN32 1 // Must use inline NTSTATUS or it will call the wrapped function twice.
+#pragma warning(push)
+#pragma warning(disable : 4430) // Must disable 4430 "default int" warning for C++ because ntstatus.h is inflexible SDK definition.
+#include
+#pragma warning(pop)
+
+#include
+
+#include "../host/conddkrefs.h"
+
+// This includes support libraries from the CRT, STL, WIL, and GSL
+#include "LibraryIncludes.h"
+
+#include
diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp
new file mode 100644
index 00000000000..14a1730da92
--- /dev/null
+++ b/src/winconpty/winconpty.cpp
@@ -0,0 +1,384 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "precomp.h"
+
+#include "winconpty.h"
+
+#ifdef __INSIDE_WINDOWS
+#include
+#include
+// You need kernelbasestaging.h to be able to use wil in libraries consumed by kernelbase.dll
+#include
+#define RESOURCE_SUPPRESS_STL
+#define WIL_SUPPORT_BITOPERATION_PASCAL_NAMES
+#include
+#else
+#include "device.h"
+#include
+#endif // __INSIDE_WINDOWS
+
+#pragma warning(push)
+#pragma warning(disable : 4273) // inconsistent dll linkage (we are exporting things kernel32 also exports)
+
+// Function Description:
+// - Returns the path to either conhost.exe or the side-by-side OpenConsole, depending on whether this
+// module is building with Windows.
+// Return Value:
+// - A pointer to permanent storage containing the path to the console host.
+static wchar_t* _ConsoleHostPath()
+{
+ // Use the magic of magic statics to only calculate this once.
+ static wil::unique_process_heap_string consoleHostPath = []() {
+#ifdef __INSIDE_WINDOWS
+ wil::unique_process_heap_string systemDirectory;
+ wil::GetSystemDirectoryW(systemDirectory);
+ return wil::str_concat_failfast(L"\\\\?\\", systemDirectory, L"\\conhost.exe");
+#else
+ // Use the STL only if we're not building in Windows.
+ std::filesystem::path modulePath{ wil::GetModuleFileNameW(wil::GetModuleInstanceHandle()) };
+ modulePath.replace_filename(L"OpenConsole.exe");
+ auto modulePathAsString{ modulePath.wstring() };
+ return wil::make_process_heap_string_nothrow(modulePathAsString.data(), modulePathAsString.size());
+#endif // __INSIDE_WINDOWS
+ }();
+ return consoleHostPath.get();
+}
+
+static bool _HandleIsValid(HANDLE h) noexcept
+{
+ return (h != INVALID_HANDLE_VALUE) && (h != nullptr);
+}
+
+HRESULT _CreatePseudoConsole(const HANDLE hToken,
+ const COORD size,
+ const HANDLE hInput,
+ const HANDLE hOutput,
+ const DWORD dwFlags,
+ _Inout_ PseudoConsole* pPty)
+{
+ if (pPty == NULL)
+ {
+ return E_INVALIDARG;
+ }
+ if (size.X == 0 || size.Y == 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ wil::unique_handle serverHandle;
+ RETURN_IF_NTSTATUS_FAILED(CreateServerHandle(serverHandle.addressof(), TRUE));
+
+ wil::unique_handle signalPipeConhostSide;
+ wil::unique_handle signalPipeOurSide;
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ // Mark inheritable for signal handle when creating. It'll have the same value on the other side.
+ sa.bInheritHandle = FALSE;
+ sa.lpSecurityDescriptor = NULL;
+
+ RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(signalPipeConhostSide.addressof(), signalPipeOurSide.addressof(), &sa, 0));
+ RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
+
+ const wchar_t* pwszFormat = L"%s --headless %s--width %hu --height %hu --signal 0x%x --server 0x%x";
+ // This is plenty of space to hold the formatted string
+ wchar_t cmd[MAX_PATH];
+ const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR;
+ swprintf_s(cmd,
+ MAX_PATH,
+ pwszFormat,
+ _ConsoleHostPath(),
+ bInheritCursor ? L"--inheritcursor " : L"",
+ size.X,
+ size.Y,
+ signalPipeConhostSide.get(),
+ serverHandle.get());
+
+ STARTUPINFOEXW siEx{ 0 };
+ siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
+ siEx.StartupInfo.hStdInput = hInput;
+ siEx.StartupInfo.hStdOutput = hOutput;
+ siEx.StartupInfo.hStdError = hOutput;
+ siEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Only pass the handles we actually want the conhost to know about to it:
+ const size_t INHERITED_HANDLES_COUNT = 4;
+ HANDLE inheritedHandles[INHERITED_HANDLES_COUNT];
+ inheritedHandles[0] = serverHandle.get();
+ inheritedHandles[1] = hInput;
+ inheritedHandles[2] = hOutput;
+ inheritedHandles[3] = signalPipeConhostSide.get();
+
+ // Get the size of the attribute list. We need one attribute, the handle list.
+ SIZE_T listSize = 0;
+ InitializeProcThreadAttributeList(NULL, 1, 0, &listSize);
+
+ // I have to use a HeapAlloc here because kernelbase can't link new[] or delete[]
+ PPROC_THREAD_ATTRIBUTE_LIST attrList = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, listSize));
+ RETURN_IF_NULL_ALLOC(attrList);
+ auto attrListDelete = wil::scope_exit([&] {
+ HeapFree(GetProcessHeap(), 0, attrList);
+ });
+
+ siEx.lpAttributeList = attrList;
+ RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &listSize));
+ // Set cleanup data for ProcThreadAttributeList when successful.
+ auto cleanupProcThreadAttribute = wil::scope_exit([&] {
+ DeleteProcThreadAttributeList(siEx.lpAttributeList);
+ });
+ RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(siEx.lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ inheritedHandles,
+ (INHERITED_HANDLES_COUNT * sizeof(HANDLE)),
+ NULL,
+ NULL));
+ wil::unique_process_information pi;
+ { // wow64 disabled filesystem redirection scope
+#if defined(BUILD_WOW6432)
+ PVOID RedirectionFlag;
+ RETURN_IF_NTSTATUS_FAILED(RtlWow64EnableFsRedirectionEx(
+ WOW64_FILE_SYSTEM_DISABLE_REDIRECT,
+ &RedirectionFlag));
+ auto resetFsRedirection = wil::scope_exit([&] {
+ RtlWow64EnableFsRedirectionEx(RedirectionFlag, &RedirectionFlag);
+ });
+#endif
+ if (hToken == INVALID_HANDLE_VALUE || hToken == NULL)
+ {
+ // Call create process
+ RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(NULL,
+ cmd,
+ NULL,
+ NULL,
+ TRUE,
+ EXTENDED_STARTUPINFO_PRESENT,
+ NULL,
+ NULL,
+ &siEx.StartupInfo,
+ pi.addressof()));
+ }
+ else
+ {
+ // Call create process
+ RETURN_IF_WIN32_BOOL_FALSE(CreateProcessAsUserW(hToken,
+ NULL,
+ cmd,
+ NULL,
+ NULL,
+ TRUE,
+ EXTENDED_STARTUPINFO_PRESENT,
+ NULL,
+ NULL,
+ &siEx.StartupInfo,
+ pi.addressof()));
+ }
+ }
+
+ // Move the process handle out of the PROCESS_INFORMATION into out Pseudoconsole
+ pPty->hConPtyProcess = pi.hProcess;
+ pi.hProcess = NULL;
+
+ RETURN_IF_NTSTATUS_FAILED(CreateClientHandle(&pPty->hPtyReference,
+ serverHandle.get(),
+ L"\\Reference",
+ FALSE));
+
+ pPty->hSignal = signalPipeOurSide.release();
+
+ return S_OK;
+}
+
+// Function Description:
+// - Resizes the conpty
+// Arguments:
+// - hSignal: A signal pipe as returned by CreateConPty.
+// - size: The new dimenstions of the conpty, in characters.
+// Return Value:
+// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
+// write the resize message to the pty.
+HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size)
+{
+ if (pPty == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ unsigned short signalPacket[3];
+ signalPacket[0] = PTY_SIGNAL_RESIZE_WINDOW;
+ signalPacket[1] = size.X;
+ signalPacket[2] = size.Y;
+
+ BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), NULL, NULL);
+ return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
+}
+
+// Function Description:
+// - This closes each of the members of a PseudoConsole. It does not free the
+// data associated with the PseudoConsole. This is helpful for testing,
+// where we might stack allocate a PseudoConsole (instead of getting a
+// HPCON via the API).
+// Arguments:
+// - pPty: A pointer to a PseudoConsole struct.
+// Return Value:
+// -
+void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty)
+{
+ if (pPty != NULL)
+ {
+ // See MSFT:19918626
+ // First break the signal pipe - this will trigger conhost to tear itself down
+ if (_HandleIsValid(pPty->hSignal))
+ {
+ CloseHandle(pPty->hSignal);
+ pPty->hSignal = 0;
+ }
+ // Then, wait on the conhost process before killing it.
+ // We do this to make sure the conhost finishes flushing any output it
+ // has yet to send before we hard kill it.
+ if (_HandleIsValid(pPty->hConPtyProcess))
+ {
+ // If the conhost is already dead, then that's fine. Presumably
+ // it's finished flushing it's output already.
+ DWORD dwExit = 0;
+ // If GetExitCodeProcess failed, it's likely conhost is already dead
+ // If so, skip waiting regardless of whatever error
+ // GetExitCodeProcess returned.
+ // We'll just go straight to killing conhost.
+ if (GetExitCodeProcess(pPty->hConPtyProcess, &dwExit) && dwExit == STILL_ACTIVE)
+ {
+ WaitForSingleObject(pPty->hConPtyProcess, INFINITE);
+ }
+
+ TerminateProcess(pPty->hConPtyProcess, 0);
+ pPty->hConPtyProcess = 0;
+ }
+ // Then take care of the reference handle.
+ // TODO GH#1810: Closing the reference handle late leaves conhost thinking
+ // that we have an outstanding connected client.
+ if (_HandleIsValid(pPty->hPtyReference))
+ {
+ CloseHandle(pPty->hPtyReference);
+ pPty->hPtyReference = 0;
+ }
+ }
+}
+
+// Function Description:
+// - This closes each of the members of a PseudoConsole, and HeapFree's the
+// memory allocated to it. This should be used to cleanup any
+// PseudoConosles that were created with CreatePseudoConsole.
+// Arguments:
+// - pPty: A pointer to a PseudoConsole struct.
+// Return Value:
+// -
+VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty)
+{
+ if (pPty != NULL)
+ {
+ _ClosePseudoConsoleMembers(pPty);
+ HeapFree(GetProcessHeap(), 0, pPty);
+ }
+}
+
+// These functions are defined in the console l1 apiset, which is generated from
+// the consoleapi.apx file in minkernel\apiset\libs\Console.
+
+// Function Description:
+// Creates a "Pseudo-console" (conpty) with dimensions (in characters)
+// provided by the `size` parameter. The caller should provide two handles:
+// - `hInput` is used for writing input to the pty, encoded as UTF-8 and VT sequences.
+// - `hOutput` is used for reading the output of the pty, encoded as UTF-8 and VT sequences.
+// Once the call completes, `phPty` will receive a token value to identify this
+// conpty object. This value should be used in conjunction with the other
+// Pseudoconsole API's.
+// `dwFlags` is used to specify optional behavior to the created pseudoconsole.
+// The flags can be combinations of the following values:
+// INHERIT_CURSOR: This will cause the created conpty to attempt to inherit the
+// cursor position of the parent terminal application. This can be useful
+// for applications like `ssh`, where ssh (currently running in a terminal)
+// might want to create a pseudoterminal session for an child application
+// and the child inherit the cursor position of ssh.
+// The creted conpty will immediately emit a "Device Status Request" VT
+// sequence to hOutput, that should be replied to on hInput in the format
+// "\x1b[;R", where `` is the row and `` is the column of the
+// cursor position.
+// This requires a cooperating terminal application - if a caller does not
+// reply to this message, the conpty will not process any input until it
+// does. Most *nix terminals and the Windows Console (after Windows 10
+// Anniversary Update) will be able to handle such a message.
+HRESULT WINAPI CreatePseudoConsole(_In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC)
+{
+ return CreatePseudoConsoleAsUser(INVALID_HANDLE_VALUE, size, hInput, hOutput, dwFlags, phPC);
+}
+
+HRESULT CreatePseudoConsoleAsUser(_In_ HANDLE hToken,
+ _In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC)
+{
+ if (phPC == NULL)
+ {
+ return E_INVALIDARG;
+ }
+ *phPC = NULL;
+ if ((!_HandleIsValid(hInput)) && (!_HandleIsValid(hOutput)))
+ {
+ return E_INVALIDARG;
+ }
+
+ PseudoConsole* pPty = (PseudoConsole*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PseudoConsole));
+ RETURN_IF_NULL_ALLOC(pPty);
+ auto cleanupPty = wil::scope_exit([&] {
+ _ClosePseudoConsole(pPty);
+ });
+
+ wil::unique_handle duplicatedInput;
+ wil::unique_handle duplicatedOutput;
+ RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hInput, GetCurrentProcess(), duplicatedInput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
+ RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hOutput, GetCurrentProcess(), duplicatedOutput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
+
+ RETURN_IF_FAILED(_CreatePseudoConsole(hToken, size, duplicatedInput.get(), duplicatedOutput.get(), dwFlags, pPty));
+
+ *phPC = (HPCON)pPty;
+ cleanupPty.release();
+
+ return S_OK;
+}
+
+// Function Description:
+// Resizes the given conpty to the specified size, in characters.
+HRESULT WINAPI ResizePseudoConsole(_In_ HPCON hPC, _In_ COORD size)
+{
+ PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ HRESULT hr = pPty == NULL ? E_INVALIDARG : S_OK;
+ if (SUCCEEDED(hr))
+ {
+ hr = _ResizePseudoConsole(pPty, size);
+ }
+ return hr;
+}
+
+// Function Description:
+// Closes the conpty and all associated state.
+// Client applications attached to the conpty will also behave as though the
+// console window they were running in was closed.
+// This can fail if the conhost hosting the pseudoconsole failed to be
+// terminated, or if the pseudoconsole was already terminated.
+VOID WINAPI ClosePseudoConsole(_In_ HPCON hPC)
+{
+ PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ if (pPty != NULL)
+ {
+ _ClosePseudoConsole(pPty);
+ }
+}
+
+#pragma warning(pop)
diff --git a/src/winconpty/winconpty.def b/src/winconpty/winconpty.def
new file mode 100644
index 00000000000..51ccf32791b
--- /dev/null
+++ b/src/winconpty/winconpty.def
@@ -0,0 +1,4 @@
+EXPORTS
+ CreatePseudoConsole
+ ResizePseudoConsole
+ ClosePseudoConsole
diff --git a/src/winconpty/winconpty.h b/src/winconpty/winconpty.h
new file mode 100644
index 00000000000..2cfea5375fd
--- /dev/null
+++ b/src/winconpty/winconpty.h
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "precomp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _PseudoConsole
+{
+ HANDLE hSignal;
+ HANDLE hPtyReference;
+ HANDLE hConPtyProcess;
+} PseudoConsole;
+
+// Signals
+// These are not defined publicly, but are used for controlling the conpty via
+// the signal pipe.
+#define PTY_SIGNAL_RESIZE_WINDOW (8u)
+
+// Implementations of the various PseudoConsole functions.
+HRESULT _CreatePseudoConsole(const HANDLE hToken,
+ const COORD size,
+ const HANDLE hInput,
+ const HANDLE hOutput,
+ const DWORD dwFlags,
+ _Inout_ PseudoConsole* pPty);
+
+HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size);
+void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty);
+VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty);
+
+HRESULT CreatePseudoConsoleAsUser(_In_ HANDLE hToken,
+ _In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/winconpty/winconpty.vcxproj b/src/winconpty/winconpty.vcxproj
new file mode 100644
index 00000000000..6ffd09468e0
--- /dev/null
+++ b/src/winconpty/winconpty.vcxproj
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+ %(AdditionalIncludeDirectories)
+
+
+
+ {58a03bb2-df5a-4b66-91a0-7ef3ba01269a}
+ Win32Proj
+ winconpty
+ winconpty
+ conpty
+
+
+
+
+
+
+
+ winconpty.def
+
+
+ onecoreuap_apiset.lib;%(AdditionalDependencies)
+
+
+