diff --git a/.github/actions/spelling/expect/alphabet.txt b/.github/actions/spelling/expect/alphabet.txt
index bb8a43b36fe..6156f69d1df 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 8107fae626f..a46d753a1bb 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -1849,7 +1849,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 bedc7b117b6..dc01dc66b14 100644
--- a/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs
+++ b/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs
@@ -33,7 +33,7 @@ public Terminal()
}
///
- /// 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 df4bb217342..10df9364e10 100644
--- a/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs
+++ b/samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs
@@ -41,7 +41,7 @@ private static void EnableVirtualTerminalSequenceProcessing()
}
///
- /// 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 10fe7e75cb2..fe8d0bb6fa7 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 5c03e61cb5a..edb05aa7bc3 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 02ed1df0d1b..34c943b3749 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 4c9e7612bb2..cd46e446d8f 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 c86b543005b..da19d1e314e 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 fec67c970c3..92bd0961319 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 7f728e5ad94..093d1fcee81 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()
}
}
@@ -1989,6 +2004,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 794fd0e1568..0c1bed61371 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -302,6 +302,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 27611190315..0138921fd15 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 396f4a095f0..2e173e0a381 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 a88fa2ee6b2..036dccb4957 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 3eee9fc8d02..f53b4a56e59 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 12b19da1f05..639c52c8509 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 d043e5472c4..19fc4381efc 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 6e4e24bdaa8..e053425cb8d 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 712b757ef52..4adac9a8056 100644
--- a/src/cascadia/WindowsTerminal/AppHost.h
+++ b/src/cascadia/WindowsTerminal/AppHost.h
@@ -74,6 +74,7 @@ class AppHost
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 79c356908ba..d06799658ac 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 94799aa0d55..c5283dca447 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 a88572f31de..bd3ac59cab9 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 fbf7fa13b3d..5ef63295517 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)