From 27582a9186c39ea9f5caf070e08fd80e50e1d587 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Mon, 24 May 2021 14:56:46 -0700 Subject: [PATCH] [Defapp] Use real HPCON for PTY management; Have Monarch always listen for connections (#10170) [Defapp] Use real HPCON for PTY management; Have Monarch always listen for connections ## PR Checklist * [x] Closes #9464 * [x] Related to #9475 - incomplete fix * [x] I work here. * [x] Manual test ## Detailed Description of the Pull Request / Additional comments - Sometimes peasants can't manage to accept a connection appropriately because I wrote defterm before @zadjii-msft's monarch/peasant architecture. The simple solution here is to just make the monarch always be listening for inbound connections. Then COM won't start a peasant with -Embedding just to ask the monarch where it should go. It'll just join the active window. I didn't close 9475 because it should follow monarch policies on which window to join... and it doesn't yet. - A lot of interesting things are happening because this didn't have a real HPCON. So I passed through the remaining handles (and re-GUID-ed the interface) that made it possible for me to pack the right process handles and such into an HPCON on the inbound connection and monitor that like any other ConptyConnection. This should resolve some of the process exit behaviors and signal channel things like resizing. --- .github/actions/spelling/expect/alphabet.txt | 1 + .github/actions/spelling/expect/expect.txt | 1 - .../GUIConsole/GUIConsole.ConPTY/Terminal.cs | 2 +- samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs | 2 +- .../CascadiaPackage/Package-Dev.appxmanifest | 2 +- .../CascadiaPackage/Package-Pre.appxmanifest | 2 +- .../CascadiaPackage/Package.appxmanifest | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 14 ++++- src/cascadia/TerminalApp/AppLogic.h | 2 + src/cascadia/TerminalApp/AppLogic.idl | 2 + src/cascadia/TerminalApp/TerminalPage.cpp | 52 +++++++++++++------ src/cascadia/TerminalApp/TerminalPage.h | 2 + .../TerminalConnection/CTerminalHandoff.cpp | 12 +++-- .../TerminalConnection/CTerminalHandoff.h | 6 ++- .../TerminalConnection/ConptyConnection.cpp | 8 +-- .../TerminalConnection/ConptyConnection.h | 4 +- src/cascadia/TerminalControl/TermControl.cpp | 5 +- .../TerminalSettingsEditor/Launch.xaml | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 8 +++ src/cascadia/WindowsTerminal/AppHost.h | 1 + src/host/proxy/ITerminalHandoff.idl | 4 +- src/host/srvinit.cpp | 11 ++++ src/inc/conpty-static.h | 2 + src/winconpty/winconpty.cpp | 31 +++++++++++ 24 files changed, 143 insertions(+), 35 deletions(-) diff --git a/.github/actions/spelling/expect/alphabet.txt b/.github/actions/spelling/expect/alphabet.txt index bb8a43b36..6156f69d1 100644 --- a/.github/actions/spelling/expect/alphabet.txt +++ b/.github/actions/spelling/expect/alphabet.txt @@ -27,6 +27,7 @@ BBBBBBBBBBBBBBDDDD BBBBBCCC BBBBCCCCC BBGGRR +CCE EFG EFGh QQQQQQQQQQABCDEFGHIJ diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index fb976b366..c092347e4 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1850,7 +1850,6 @@ psp PSPCB psr PSTR -psuedoconsole psz ptch ptr diff --git a/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs b/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs index bedc7b117..dc01dc66b 100644 --- a/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs +++ b/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs @@ -33,7 +33,7 @@ namespace GUIConsole.ConPTY } /// - /// Start the psuedoconsole and run the process as shown in + /// Start the pseudoconsole and run the process as shown in /// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole /// /// the command to run, e.g. cmd.exe diff --git a/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs b/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs index df4bb2173..10df9364e 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs @@ -41,7 +41,7 @@ namespace MiniTerm } /// - /// Start the psuedoconsole and run the process as shown in + /// Start the pseudoconsole and run the process as shown in /// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole /// /// the command to run, e.g. cmd.exe diff --git a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest index 10fe7e75c..fe8d0bb6f 100644 --- a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest @@ -99,7 +99,7 @@ - + diff --git a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest index 5c03e61cb..edb05aa7b 100644 --- a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest @@ -100,7 +100,7 @@ - + diff --git a/src/cascadia/CascadiaPackage/Package.appxmanifest b/src/cascadia/CascadiaPackage/Package.appxmanifest index 02ed1df0d..34c943b37 100644 --- a/src/cascadia/CascadiaPackage/Package.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package.appxmanifest @@ -100,7 +100,7 @@ - + --> diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 4c9e7612b..cd46e446d 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1206,13 +1206,25 @@ namespace winrt::TerminalApp::implementation // in and be routed to an event with no handlers or a non-ready Page. if (_appArgs.IsHandoffListener()) { - _root->SetInboundListener(); + SetInboundListener(); } } return result; } + // Method Description: + // - Triggers the setup of the listener for incoming console connections + // from the operating system. + // Arguments: + // - + // Return Value: + // - + void AppLogic::SetInboundListener() + { + _root->SetInboundListener(); + } + // Method Description: // - Parse the provided commandline arguments into actions, and try to // perform them immediately. diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index c86b54300..da19d1e31 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -79,6 +79,8 @@ namespace winrt::TerminalApp::implementation Windows::UI::Xaml::UIElement GetRoot() noexcept; + void SetInboundListener(); + hstring Title(); void TitlebarClicked(); bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index fec67c970..92bd09613 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -42,6 +42,8 @@ namespace TerminalApp void LoadSettings(); Windows.UI.Xaml.UIElement GetRoot(); + void SetInboundListener(); + String Title { get; }; Boolean FocusMode { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 91dd1ed70..41be3b846 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -325,23 +325,38 @@ namespace winrt::TerminalApp::implementation // This MUST be done after we've registered the event listener for the new connections // or the COM server might start receiving requests on another thread and dispatch // them to nowhere. - if (_shouldStartInboundListener) + _StartInboundListener(); + } + } + + // Routine Description: + // - Will start the listener for inbound console handoffs if we have already determined + // that we should do so. + // NOTE: Must be after TerminalPage::_OnNewConnection has been connected up. + // Arguments: + // - - Looks at _shouldStartInboundListener + // Return Value: + // - - May fail fast if setup fails as that would leave us in a weird state. + void TerminalPage::_StartInboundListener() + { + if (_shouldStartInboundListener) + { + _shouldStartInboundListener = false; + + try { - try - { - winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::StartInboundListener(); - } - // If we failed to start the listener, it will throw. - // We should fail fast here or the Terminal will be in a very strange state. - // We only start the listener if the Terminal was started with the COM server - // `-Embedding` flag and we make no tabs as a result. - // Therefore, if the listener cannot start itself up to make that tab with - // the inbound connection that caused the COM activation in the first place... - // we would be left with an empty terminal frame with no tabs. - // Instead, crash out so COM sees the server die and things unwind - // without a weird empty frame window. - CATCH_FAIL_FAST() + winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::StartInboundListener(); } + // If we failed to start the listener, it will throw. + // We should fail fast here or the Terminal will be in a very strange state. + // We only start the listener if the Terminal was started with the COM server + // `-Embedding` flag and we make no tabs as a result. + // Therefore, if the listener cannot start itself up to make that tab with + // the inbound connection that caused the COM activation in the first place... + // we would be left with an empty terminal frame with no tabs. + // Instead, crash out so COM sees the server die and things unwind + // without a weird empty frame window. + CATCH_FAIL_FAST() } } @@ -1972,6 +1987,13 @@ namespace winrt::TerminalApp::implementation void TerminalPage::SetInboundListener() { _shouldStartInboundListener = true; + + // If the page has already passed the NotInitialized state, + // then it is ready-enough for us to just start this immediately. + if (_startupState != StartupState::NotInitialized) + { + _StartInboundListener(); + } } winrt::TerminalApp::IDialogPresenter TerminalPage::DialogPresenter() const diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 37a1af8ab..a034b2b5f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -301,6 +301,8 @@ namespace winrt::TerminalApp::implementation void _SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor); void _ClearNewTabButtonColor(); + void _StartInboundListener(); + void _CompleteInitialization(); void _FocusActiveControl(IInspectable sender, IInspectable eventArgs); diff --git a/src/cascadia/TerminalConnection/CTerminalHandoff.cpp b/src/cascadia/TerminalConnection/CTerminalHandoff.cpp index 276111903..0138921fd 100644 --- a/src/cascadia/TerminalConnection/CTerminalHandoff.cpp +++ b/src/cascadia/TerminalConnection/CTerminalHandoff.cpp @@ -84,12 +84,14 @@ static HRESULT _duplicateHandle(const HANDLE in, HANDLE& out) noexcept // - in - PTY input handle that we will read from // - out - PTY output handle that we will write to // - signal - PTY signal handle for out of band messaging -// - process - Process handle to client so we can track its lifetime and exit appropriately +// - ref - Client reference handle for console session so it stays alive until we let go +// - server - PTY process handle to track for lifetime/cleanup +// - client - Process handle to client so we can track its lifetime and exit appropriately // Return Value: // - E_NOT_VALID_STATE if a event handler is not registered before calling. `::DuplicateHandle` // error codes if we cannot manage to make our own copy of handles to retain. Or S_OK/error // from the registered handler event function. -HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE process) noexcept +HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept { // Report an error if no one registered a handoff function before calling this. RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff); @@ -101,8 +103,10 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE sign RETURN_IF_FAILED(_duplicateHandle(in, in)); RETURN_IF_FAILED(_duplicateHandle(out, out)); RETURN_IF_FAILED(_duplicateHandle(signal, signal)); - RETURN_IF_FAILED(_duplicateHandle(process, process)); + RETURN_IF_FAILED(_duplicateHandle(ref, ref)); + RETURN_IF_FAILED(_duplicateHandle(server, server)); + RETURN_IF_FAILED(_duplicateHandle(client, client)); // Call registered handler from when we started listening. - return _pfnHandoff(in, out, signal, process); + return _pfnHandoff(in, out, signal, ref, server, client); } diff --git a/src/cascadia/TerminalConnection/CTerminalHandoff.h b/src/cascadia/TerminalConnection/CTerminalHandoff.h index 396f4a095..2e173e0a3 100644 --- a/src/cascadia/TerminalConnection/CTerminalHandoff.h +++ b/src/cascadia/TerminalConnection/CTerminalHandoff.h @@ -26,7 +26,7 @@ Author(s): #define __CLSID_CTerminalHandoff "051F34EE-C1FD-4B19-AF75-9BA54648434C" #endif -using NewHandoffFunction = HRESULT (*)(HANDLE, HANDLE, HANDLE, HANDLE); +using NewHandoffFunction = HRESULT (*)(HANDLE, HANDLE, HANDLE, HANDLE, HANDLE, HANDLE); struct __declspec(uuid(__CLSID_CTerminalHandoff)) CTerminalHandoff : public Microsoft::WRL::RuntimeClass, ITerminalHandoff> @@ -35,7 +35,9 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff)) STDMETHODIMP EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, - HANDLE process) noexcept override; + HANDLE ref, + HANDLE server, + HANDLE client) noexcept override; #pragma endregion diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index a88fa2ee6..036dccb49 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -194,6 +194,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation ConptyConnection::ConptyConnection(const HANDLE hSig, const HANDLE hIn, const HANDLE hOut, + const HANDLE hRef, + const HANDLE hServerProcess, const HANDLE hClientProcess) : _initialRows{ 25 }, _initialCols{ 80 }, @@ -208,7 +210,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _inPipe{ hIn }, _outPipe{ hOut } { - hSig; // TODO: GH 9464 this needs to be packed into the hpcon + THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC)); if (_guid == guid{}) { _guid = Utils::CreateGuid(); @@ -500,10 +502,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation winrt::event_token ConptyConnection::NewConnection(NewConnectionHandler const& handler) { return _newConnectionHandlers.add(handler); }; void ConptyConnection::NewConnection(winrt::event_token const& token) { _newConnectionHandlers.remove(token); }; - HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE process) noexcept + HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept try { - auto conn = winrt::make(signal, in, out, process); + auto conn = winrt::make(signal, in, out, ref, server, client); _newConnectionHandlers(conn); return S_OK; diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index 3eee9fc8d..f53b4a56e 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -22,6 +22,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation ConptyConnection(const HANDLE hSig, const HANDLE hIn, const HANDLE hOut, + const HANDLE hRef, + const HANDLE hServerProcess, const HANDLE hClientProcess); ConptyConnection( @@ -54,7 +56,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation void _indicateExitWithStatus(unsigned int status) noexcept; void _ClientTerminated() noexcept; - static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE process) noexcept; + static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept; uint32_t _initialRows{}; uint32_t _initialCols{}; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 12b19da1f..639c52c85 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1664,7 +1664,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_CursorPositionChanged(const IInspectable& /*sender*/, const IInspectable& /*args*/) { - _tsfTryRedrawCanvas->Run(); + if (_tsfTryRedrawCanvas) + { + _tsfTryRedrawCanvas->Run(); + } } hstring TermControl::Title() diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml index d043e5472..19fc4381e 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.xaml +++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml @@ -89,7 +89,7 @@ - + diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6e4e24bda..e053425cb 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -640,6 +640,14 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s const winrt::Windows::Foundation::IInspectable& /*args*/) { _setupGlobalHotkeys(); + + // The monarch is just going to be THE listener for inbound connections. + _listenForInboundConnections(); +} + +void AppHost::_listenForInboundConnections() +{ + _logic.SetInboundListener(); } winrt::fire_and_forget AppHost::_setupGlobalHotkeys() diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 712b757ef..4adac9a80 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -74,6 +74,7 @@ private: bool _LazyLoadDesktopManager(); + void _listenForInboundConnections(); winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, diff --git a/src/host/proxy/ITerminalHandoff.idl b/src/host/proxy/ITerminalHandoff.idl index 79c356908..d06799658 100644 --- a/src/host/proxy/ITerminalHandoff.idl +++ b/src/host/proxy/ITerminalHandoff.idl @@ -6,11 +6,13 @@ import "ocidl.idl"; [ object, - uuid(FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74) + uuid(59D55CCE-FC8A-48B4-ACE8-0A9286C6557F) ] interface ITerminalHandoff : IUnknown { HRESULT EstablishPtyHandoff([in, system_handle(sh_pipe)] HANDLE in, [in, system_handle(sh_pipe)] HANDLE out, [in, system_handle(sh_pipe)] HANDLE signal, + [in, system_handle(sh_file)] HANDLE ref, + [in, system_handle(sh_process)] HANDLE server, [in, system_handle(sh_process)] HANDLE client); }; diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 94799aa0d..c5283dca4 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -14,6 +14,7 @@ #include "../types/inc/GlyphWidth.hpp" +#include "../server/DeviceHandle.h" #include "../server/Entrypoints.h" #include "../server/IoSorter.h" @@ -414,6 +415,14 @@ try wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, TRUE, static_cast(connectMessage->Descriptor.Process)) }; RETURN_LAST_ERROR_IF_NULL(clientProcess.get()); + wil::unique_handle refHandle; + RETURN_IF_NTSTATUS_FAILED(DeviceHandle::CreateClientHandle(refHandle.addressof(), + Server, + L"\\Reference", + FALSE)); + + const auto serverProcess = GetCurrentProcess(); + ::Microsoft::WRL::ComPtr handoff; RETURN_IF_FAILED(CoCreateInstance(g.handoffTerminalClsid.value(), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&handoff))); @@ -421,6 +430,8 @@ try RETURN_IF_FAILED(handoff->EstablishPtyHandoff(inPipeTheirSide.get(), outPipeTheirSide.get(), signalPipeTheirSide.get(), + refHandle.get(), + serverProcess, clientProcess.get())); inPipeTheirSide.release(); diff --git a/src/inc/conpty-static.h b/src/inc/conpty-static.h index a88572f31..bd3ac59ca 100644 --- a/src/inc/conpty-static.h +++ b/src/inc/conpty-static.h @@ -24,6 +24,8 @@ HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size); VOID WINAPI ConptyClosePseudoConsole(HPCON hPC); +HRESULT WINAPI ConptyPackPseudoConsole(HANDLE hServerProcess, HANDLE hRef, HANDLE hSignal, HPCON* phPC); + #ifdef __cplusplus } #endif diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp index fbf7fa13b..5ef632955 100644 --- a/src/winconpty/winconpty.cpp +++ b/src/winconpty/winconpty.cpp @@ -400,4 +400,35 @@ extern "C" VOID WINAPI ConptyClosePseudoConsole(_In_ HPCON hPC) } } +// NOTE: This one is not defined in the Windows headers but is +// necessary for our outside recipient in the Terminal +// to set up a PTY session in fundamentally the same way as the +// Creation functions. Using the same HPCON pack enables +// resizing and closing to "just work." + +// Function Description: +// Packs loose handle information for an inbound ConPTY +// session into the same HPCON as a created session. +extern "C" HRESULT WINAPI ConptyPackPseudoConsole(_In_ HANDLE hProcess, + _In_ HANDLE hRef, + _In_ HANDLE hSignal, + _Out_ HPCON* phPC) +{ + RETURN_HR_IF(E_INVALIDARG, nullptr == phPC); + *phPC = nullptr; + RETURN_HR_IF(E_INVALIDARG, !_HandleIsValid(hProcess)); + RETURN_HR_IF(E_INVALIDARG, !_HandleIsValid(hRef)); + RETURN_HR_IF(E_INVALIDARG, !_HandleIsValid(hSignal)); + + PseudoConsole* pPty = (PseudoConsole*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PseudoConsole)); + RETURN_IF_NULL_ALLOC(pPty); + + pPty->hConPtyProcess = hProcess; + pPty->hPtyReference = hRef; + pPty->hSignal = hSignal; + + *phPC = (HPCON)pPty; + return S_OK; +} + #pragma warning(pop)