906edf7002
- Implements the default application behavior and handoff mechanisms between console and terminal. The inbox portion is done already. This adds the ability for our OpenConsole.exe to accept the incoming server connection from the Windows OS, stand up a PTY session, start the Windows Terminal as a listener for an incoming connection, and then send it the incoming PTY connection for it to launch a tab. - The tab is launched with default settings at the moment. - You must configure the default application using the `conhost.exe` propsheet or with the registry keys. Finishing the setting inside Windows Terminal will be a todo after this is complete. The OS Settings panel work to surface this setting is a dependency delivered by another team and you will not see it here. ## Validation Steps Performed - [x] Manual adjust of registry keys to the delegation conhost/terminal behavior - [x] Adjustment of the delegation options with the propsheet - [x] Launching things from the run box manually and watching them show in Terminal - [x] Launching things from shortcuts and watching them show in the Terminal Documentation on how it works will be a TODO post completion in #9462 References #7414 - Default Terminal spec Closes #492
109 lines
4.1 KiB
C++
109 lines
4.1 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
|
|
#include "CTerminalHandoff.h"
|
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
// The callback function when a connection is received
|
|
static NewHandoffFunction _pfnHandoff = nullptr;
|
|
// The registration ID of the class object for clean up later
|
|
static DWORD g_cTerminalHandoffRegistration = 0;
|
|
|
|
// Routine Description:
|
|
// - Starts listening for TerminalHandoff requests by registering
|
|
// our class and interface with COM.
|
|
// Arguments:
|
|
// - pfnHandoff - Function to callback when a handoff is received
|
|
// Return Value:
|
|
// - S_OK, E_NOT_VALID_STATE (start called when already started) or relevant COM registration error.
|
|
HRESULT CTerminalHandoff::s_StartListening(NewHandoffFunction pfnHandoff) noexcept
|
|
try
|
|
{
|
|
RETURN_HR_IF(E_NOT_VALID_STATE, _pfnHandoff != nullptr);
|
|
|
|
const auto classFactory = Make<SimpleClassFactory<CTerminalHandoff>>();
|
|
|
|
RETURN_IF_NULL_ALLOC(classFactory);
|
|
|
|
ComPtr<IUnknown> unk;
|
|
RETURN_IF_FAILED(classFactory.As(&unk));
|
|
|
|
RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_cTerminalHandoffRegistration));
|
|
|
|
_pfnHandoff = pfnHandoff;
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
// Routine Description:
|
|
// - Stops listening for TerminalHandoff requests by revoking the registration
|
|
// our class and interface with COM
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - S_OK, E_NOT_VALID_STATE (stop called when not started), or relevant COM class revoke error
|
|
HRESULT CTerminalHandoff::s_StopListening() noexcept
|
|
{
|
|
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
|
|
|
_pfnHandoff = nullptr;
|
|
|
|
if (g_cTerminalHandoffRegistration)
|
|
{
|
|
RETURN_IF_FAILED(CoRevokeClassObject(g_cTerminalHandoffRegistration));
|
|
g_cTerminalHandoffRegistration = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Helper to duplicate a handle to ourselves so we can keep holding onto it
|
|
// after the caller frees the original one.
|
|
// Arguments:
|
|
// - in - Handle to duplicate
|
|
// - out - Where to place the duplicated value
|
|
// Return Value:
|
|
// - S_OK or Win32 error from `::DuplicateHandle`
|
|
static HRESULT _duplicateHandle(const HANDLE in, HANDLE& out) noexcept
|
|
{
|
|
RETURN_IF_WIN32_BOOL_FALSE(::DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS));
|
|
return S_OK;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Receives the terminal handoff via COM from the other process,
|
|
// duplicates handles as COM will free those given on the way out,
|
|
// then fires off an event notifying the rest of the terminal that
|
|
// a connection is on its way in.
|
|
// Arguments:
|
|
// - 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
|
|
// 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
|
|
{
|
|
// Report an error if no one registered a handoff function before calling this.
|
|
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
|
|
|
// Duplicate the handles from what we received.
|
|
// The contract with COM specifies that any HANDLEs we receive from the caller belong
|
|
// to the caller and will be freed when we leave the scope of this method.
|
|
// Making our own duplicate copy ensures they hang around in our lifetime.
|
|
RETURN_IF_FAILED(_duplicateHandle(in, in));
|
|
RETURN_IF_FAILED(_duplicateHandle(out, out));
|
|
RETURN_IF_FAILED(_duplicateHandle(signal, signal));
|
|
RETURN_IF_FAILED(_duplicateHandle(process, process));
|
|
|
|
// Call registered handler from when we started listening.
|
|
return _pfnHandoff(in, out, signal, process);
|
|
}
|