From 3822d5b6621741af570ef181268064527c77e16a Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Thu, 11 Feb 2021 21:07:50 +0000 Subject: [PATCH] Merged PR 5677497: [Git2Git] Merged PR 5655213: Allow conhost to handoff to registered default app handler Contains: - Delegation Configurator that can lookup/edit/save configuration information to registry - Conhost can lookup the CLSID of a registered default - Conhost has the ability to handoff a starting visible-window interactive session to the registered default - Velocity key since this is a big deal and we want to be careful - IDL for the interface Related work items: MSFT-16458099 Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev 0ca55027d8180fbbaa145f2fe7a15005856c0f7c --- OpenConsole.sln | 33 +++ src/host/dirs | 3 +- src/host/dll/host.vcxproj | 81 ------ src/host/globals.h | 4 + src/host/proxy/Host.Proxy.vcxproj | 37 +++ .../Host.Proxy.vcxproj.filters} | 23 +- src/host/proxy/IConsoleHandoff.idl | 26 ++ src/host/proxy/sources | 31 +++ src/host/proxy/sources.dep | 6 + src/host/srvinit.cpp | 31 ++- src/host/srvinit.h | 2 + src/inc/conint.h | 5 + src/internal/stubs.cpp | 7 + src/propslib/DelegationConfig.cpp | 230 ++++++++++++++++++ src/propslib/DelegationConfig.hpp | 48 ++++ src/propslib/propslib.vcxproj | 4 +- src/propslib/propslib.vcxproj.filters | 6 + src/propslib/sources | 2 + src/server/ConDrvDeviceComm.cpp | 9 + src/server/ConDrvDeviceComm.h | 2 + src/server/DeviceComm.h | 2 + src/server/IoDispatchers.cpp | 56 ++++- src/server/lib/server.vcxproj | 10 + src/server/lib/server.vcxproj.filters | 9 + src/server/sources.inc | 1 + 25 files changed, 557 insertions(+), 111 deletions(-) delete mode 100644 src/host/dll/host.vcxproj create mode 100644 src/host/proxy/Host.Proxy.vcxproj rename src/host/{dll/host.vcxproj.filters => proxy/Host.Proxy.vcxproj.filters} (64%) create mode 100644 src/host/proxy/IConsoleHandoff.idl create mode 100644 src/host/proxy/sources create mode 100644 src/host/proxy/sources.dep create mode 100644 src/propslib/DelegationConfig.cpp create mode 100644 src/propslib/DelegationConfig.hpp diff --git a/OpenConsole.sln b/OpenConsole.sln index 851884535e7..02329bda32d 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -364,6 +364,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\c EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Proxy", "src\host\proxy\Host.Proxy.vcxproj", "{E437B604-3E98-4F40-A927-E173E818EA4B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2558,6 +2560,36 @@ Global {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.Build.0 = Release|x64 {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.ActiveCfg = Release|Win32 {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.Build.0 = Release|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|ARM.ActiveCfg = AuditMode|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|x64.Build.0 = AuditMode|x64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.AuditMode|x86.Build.0 = AuditMode|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|ARM.ActiveCfg = Debug|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|ARM64.Build.0 = Debug|ARM64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|x64.ActiveCfg = Debug|x64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|x64.Build.0 = Debug|x64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|x86.ActiveCfg = Debug|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Debug|x86.Build.0 = Debug|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|Any CPU.ActiveCfg = Release|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|ARM.ActiveCfg = Release|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|ARM64.ActiveCfg = Release|ARM64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|ARM64.Build.0 = Release|ARM64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|x64.ActiveCfg = Release|x64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|x64.Build.0 = Release|x64 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|x86.ActiveCfg = Release|Win32 + {E437B604-3E98-4F40-A927-E173E818EA4B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2648,6 +2680,7 @@ Global {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202} {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} {4DAF0299-495E-4CD1-A982-9BAC16A45932} = {59840756-302F-44DF-AA47-441A9D673202} + {E437B604-3E98-4F40-A927-E173E818EA4B} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/host/dirs b/src/host/dirs index 7c34ce56698..f7a245ab4ca 100644 --- a/src/host/dirs +++ b/src/host/dirs @@ -1,4 +1,5 @@ -DIRS=exe \ +DIRS=proxy \ + exe \ lib \ ut_lib \ ut_host \ diff --git a/src/host/dll/host.vcxproj b/src/host/dll/host.vcxproj deleted file mode 100644 index 52fe65da4d3..00000000000 --- a/src/host/dll/host.vcxproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - {E437B604-3E98-4F40-A927-E173E818EA4B} - Win32Proj - host - Host.DLL - ConhostV2 - DynamicLibrary - - - - - - Create - ProgramDatabase - - - - - - - - - {06ec74cb-9a12-429c-b551-8562ec964846} - - - {06ec74cb-9a12-429c-b551-8532ec964726} - - - {345fd5a4-b32b-4f29-bd1c-b033bd2c35cc} - - - {af0a096a-8b3a-4949-81ef-7df8f0fee91f} - - - {1c959542-bac2-4e55-9a6d-13251914cbb9} - - - {18d09a24-8240-42d6-8cb6-236eee820262} - - - {dcf55140-ef6a-4736-a403-957e4f7430bb} - - - {3ae13314-1939-4dfa-9c14-38ca0834050c} - - - {2fd12fbb-1ddb-46d8-b818-1023c624caca} - - - {18d09a24-8240-42d6-8cb6-236eee820263} - - - {06ec74cb-9a12-429c-b551-8562ec954746} - - - - - - - - - - - - true - - - - %(PreprocessorDefinitions) - - - ConhostV2.def - true - type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*' - - - - - diff --git a/src/host/globals.h b/src/host/globals.h index f14f96251bf..2bcfd2859e5 100644 --- a/src/host/globals.h +++ b/src/host/globals.h @@ -70,6 +70,10 @@ class Globals ApiRoutines api; + bool handoffTarget = false; + + std::optional handoffConsoleClsid; + #ifdef UNIT_TESTING void EnableConptyModeForTests(std::unique_ptr vtRenderEngine); #endif diff --git a/src/host/proxy/Host.Proxy.vcxproj b/src/host/proxy/Host.Proxy.vcxproj new file mode 100644 index 00000000000..41246611578 --- /dev/null +++ b/src/host/proxy/Host.Proxy.vcxproj @@ -0,0 +1,37 @@ + + + + {E437B604-3E98-4F40-A927-E173E818EA4B} + Win32Proj + openconsoleproxy + OpenConsoleProxy + OpenConsoleProxy + Utility + + + + + + + + + IConsoleHandoff.h + NT100 + $(IntDir) + + + + + ..;%(AdditionalIncludeDirectories) + NotUsing + + + + + diff --git a/src/host/dll/host.vcxproj.filters b/src/host/proxy/Host.Proxy.vcxproj.filters similarity index 64% rename from src/host/dll/host.vcxproj.filters rename to src/host/proxy/Host.Proxy.vcxproj.filters index 6bea8807962..103c008ebed 100644 --- a/src/host/dll/host.vcxproj.filters +++ b/src/host/proxy/Host.Proxy.vcxproj.filters @@ -15,29 +15,16 @@ - - Source Files - - - Source Files - + - - Header Files - - + Header Files - + Source Files - - - - - Resource Files - + - + \ No newline at end of file diff --git a/src/host/proxy/IConsoleHandoff.idl b/src/host/proxy/IConsoleHandoff.idl new file mode 100644 index 00000000000..f9646985279 --- /dev/null +++ b/src/host/proxy/IConsoleHandoff.idl @@ -0,0 +1,26 @@ +import "oaidl.idl"; +import "ocidl.idl"; + +typedef struct _CONSOLE_PORTABLE_ATTACH_MSG +{ + DWORD IdLowPart; + LONG IdHighPart; + ULONG64 Process; + ULONG64 Object; + ULONG Function; + ULONG InputSize; + ULONG OutputSize; +} CONSOLE_PORTABLE_ATTACH_MSG; + +typedef CONSOLE_PORTABLE_ATTACH_MSG* PCONSOLE_PORTABLE_ATTACH_MSG; +typedef const CONSOLE_PORTABLE_ATTACH_MSG* PCCONSOLE_PORTABLE_ATTACH_MSG; + +[ + object, + uuid(2B607BC1-43EB-40C3-95AE-2856ADDB7F23) +] interface IConsoleHandoff : IUnknown +{ + HRESULT EstablishHandoff([in, system_handle(sh_file)] HANDLE server, + [in, system_handle(sh_event)] HANDLE inputEvent, + [in, ref] PCCONSOLE_PORTABLE_ATTACH_MSG msg); +}; diff --git a/src/host/proxy/sources b/src/host/proxy/sources new file mode 100644 index 00000000000..dc6b8db30ea --- /dev/null +++ b/src/host/proxy/sources @@ -0,0 +1,31 @@ +# ------------------------------------- +# Windows Console +# - Console Host COM Proxy +# ------------------------------------- + +# This program provides the COM call and proxy +# information for handing off one console session to another +# capable console host application. + +# ------------------------------------- +# Program Information +# ------------------------------------- + +TARGETNAME = +TARGETTYPE = NOTARGET + +# ------------------------------------- +# Build System Settings +# ------------------------------------- + +MIDL_FLAGS = $(MIDL_FLAGS) + +# ------------------------------------- +# Sources, Headers, and Libraries +# ------------------------------------- + +SOURCES = \ + IConsoleHandoff.idl \ + +INCLUDES = \ + $(INCLUDES); \ diff --git a/src/host/proxy/sources.dep b/src/host/proxy/sources.dep new file mode 100644 index 00000000000..6b254b27a36 --- /dev/null +++ b/src/host/proxy/sources.dep @@ -0,0 +1,6 @@ +PUBLIC_PASS0_CONSUMES= \ + minkernel\published\base|PASS0 \ + onecore\com\published\idlole\publish|PASS0 \ + onecore\enduser\sql\xml\msxml3\publish|PASS0 \ + onecore\inetcore\published\sdk\inc|PASS0 \ + diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 4c6ee6b1338..5ebacb48bbe 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -23,6 +23,9 @@ #include "renderData.hpp" #include "../renderer/base/renderer.hpp" +#include "../inc/conint.h" +#include "../propslib/DelegationConfig.hpp" + #pragma hdrstop using namespace Microsoft::Console::Interactivity; @@ -32,27 +35,37 @@ const UINT CONSOLE_EVENT_FAILURE_ID = 21790; const UINT CONSOLE_LPC_PORT_FAILURE_ID = 21791; [[nodiscard]] HRESULT ConsoleServerInitialization(_In_ HANDLE Server, const ConsoleArguments* const args) +try { Globals& Globals = ServiceLocator::LocateGlobals(); - try - { - Globals.pDeviceComm = new ConDrvDeviceComm(Server); + Globals.pDeviceComm = new ConDrvDeviceComm(Server); + + Globals.launchArgs = *args; - Globals.launchArgs = *args; + Globals.uiOEMCP = GetOEMCP(); + Globals.uiWindowsCP = GetACP(); - Globals.uiOEMCP = GetOEMCP(); - Globals.uiWindowsCP = GetACP(); + Globals.pFontDefaultList = new RenderFontDefaults(); - Globals.pFontDefaultList = new RenderFontDefaults(); + FontInfoBase::s_SetFontDefaultList(Globals.pFontDefaultList); - FontInfoBase::s_SetFontDefaultList(Globals.pFontDefaultList); + // Check if this conhost is allowed to delegate its activities to another. + // If so, look up the registered default console handler. + bool isEnabled = false; + if (SUCCEEDED(Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy(isEnabled) && isEnabled)) + { + IID delegationClsid; + if (SUCCEEDED(DelegationConfig::s_GetConsole(delegationClsid))) + { + Globals.handoffConsoleClsid = delegationClsid; + } } - CATCH_RETURN(); // Removed allocation of scroll buffer here. return S_OK; } +CATCH_RETURN() static bool s_IsOnDesktop() { diff --git a/src/host/srvinit.h b/src/host/srvinit.h index b660d63ed81..58fbb3035cf 100644 --- a/src/host/srvinit.h +++ b/src/host/srvinit.h @@ -26,4 +26,6 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand, [[nodiscard]] NTSTATUS ConsoleAllocateConsole(PCONSOLE_API_CONNECTINFO p); [[nodiscard]] NTSTATUS RemoveConsole(_In_ ConsoleProcessHandle* ProcessData); +[[nodiscard]] bool ConsoleConnectionDeservesVisibleWindow(PCONSOLE_API_CONNECTINFO p); + void ConsoleCheckDebug(); diff --git a/src/inc/conint.h b/src/inc/conint.h index 68d9745fcfb..e744981f99d 100644 --- a/src/inc/conint.h +++ b/src/inc/conint.h @@ -44,4 +44,9 @@ namespace Microsoft::Console::Internal { [[nodiscard]] HRESULT TrySetDarkMode(HWND hwnd) noexcept; } + + namespace DefaultApp + { + [[nodiscard]] HRESULT CheckDefaultAppPolicy(bool& isEnabled) noexcept; + } } diff --git a/src/internal/stubs.cpp b/src/internal/stubs.cpp index c7b5dcfaff5..7969e770d52 100644 --- a/src/internal/stubs.cpp +++ b/src/internal/stubs.cpp @@ -29,3 +29,10 @@ void EdpPolicy::AuditClipboard(const std::wstring_view /*destinationName*/) noex { return S_FALSE; } + +[[nodiscard]] +HRESULT DefaultApp::CheckDefaultAppPolicy(bool& isEnabled) noexcept +{ + isEnabled = false; + return S_OK; +} \ No newline at end of file diff --git a/src/propslib/DelegationConfig.cpp b/src/propslib/DelegationConfig.cpp new file mode 100644 index 00000000000..50431a8c18a --- /dev/null +++ b/src/propslib/DelegationConfig.cpp @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "DelegationConfig.hpp" + +#include "RegistrySerialization.hpp" + +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::ApplicationModel; +using namespace ABI::Windows::ApplicationModel::AppExtensions; + +#pragma hdrstop + +#define DELEGATION_CONSOLE_KEY_NAME L"DelegationConsole" +#define DELEGATION_TERMINAL_KEY_NAME L"DelegationTerminal" + +#define DELEGATION_CONSOLE_EXTENSION_NAME L"com.microsoft.windows.console.host" +#define DELEGATION_TERMINAL_EXTENSION_NAME L"com.microsoft.windows.terminal.host" + + +template::value>::type* = nullptr> +HRESULT _lookupCatalog(PCWSTR extensionName, std::vector& vec) noexcept +{ + vec.clear(); + + auto coinit = wil::CoInitializeEx(COINIT_MULTITHREADED); + + ComPtr catalogStatics; + RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_AppExtensions_AppExtensionCatalog).Get(), &catalogStatics)); + + ComPtr catalog; + RETURN_IF_FAILED(catalogStatics->Open(HStringReference(extensionName).Get(), &catalog)); + + ComPtr*>> findOperation; + RETURN_IF_FAILED(catalog->FindAllAsync(&findOperation)); + + ComPtr> extensionList; + RETURN_IF_FAILED(wil::wait_for_completion_nothrow(findOperation.Get(), &extensionList)); + + UINT extensionCount; + RETURN_IF_FAILED(extensionList->get_Size(&extensionCount)); + for (UINT index = 0; index < extensionCount; index++) + { + T extensionMetadata; + + ComPtr extension; + RETURN_IF_FAILED(extensionList->GetAt(index, &extension)); + + ComPtr extensionPackage; + RETURN_IF_FAILED(extension->get_Package(&extensionPackage)); + + ComPtr extensionPackageId; + RETURN_IF_FAILED(extensionPackage->get_Id(&extensionPackageId)); + + HString publisherId; + RETURN_IF_FAILED(extensionPackageId->get_PublisherId(publisherId.GetAddressOf())); + + // PackageId.Name + HString name; + RETURN_IF_FAILED(extensionPackageId->get_Name(name.GetAddressOf())); + + extensionMetadata.name = std::wstring{ name.GetRawBuffer(nullptr) }; + + // PackageId.Version + HString publisher; + RETURN_IF_FAILED(extensionPackageId->get_Publisher(publisher.GetAddressOf())); + + extensionMetadata.author = std::wstring{ publisher.GetRawBuffer(nullptr) }; + + // Fetch the custom properties XML out of the extension information + ComPtr> propertiesOperation; + RETURN_IF_FAILED(extension->GetExtensionPropertiesAsync(&propertiesOperation)); + + // Wait for async to complete and return the property set. + ComPtr properties; + RETURN_IF_FAILED(wil::wait_for_completion_nothrow(propertiesOperation.Get(), &properties)); + + // We can't do anything on a set, but it must also be convertible to this type of map per the Windows.Foundation specs for this + ComPtr> map; + RETURN_IF_FAILED(properties.As(&map)); + + // Looking it up is going to get us an inspectable + ComPtr inspectable; + RETURN_IF_FAILED(map->Lookup(HStringReference(L"Clsid").Get(), &inspectable)); + + // Unfortunately that inspectable is another set because we're dealing with XML data payload that we put in the manifest. + ComPtr anotherSet; + RETURN_IF_FAILED(inspectable.As(&anotherSet)); + + // And we can't look at sets directly, so move it to map. + ComPtr> anotherMap; + RETURN_IF_FAILED(anotherSet.As(&anotherMap)); + + // Use the magic value of #text to get the body between the XML tags. And of course it's an obtuse Inspectable again. + ComPtr anotherInspectable; + RETURN_IF_FAILED(anotherMap->Lookup(HStringReference(L"#text").Get(), &anotherInspectable)); + + // But this time that Inspectable is an IPropertyValue, which is a PROPVARIANT in a trenchcoat. + ComPtr propValue; + RETURN_IF_FAILED(anotherInspectable.As(&propValue)); + + // Check the type of the variant + PropertyType propType; + RETURN_IF_FAILED(propValue->get_Type(&propType)); + + // If we're not a string, bail because I don't know what's going on. + if (propType != PropertyType::PropertyType_String) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + // Get that string out. + HString value; + RETURN_IF_FAILED(propValue->GetString(value.GetAddressOf())); + + // Holy cow. It should be a GUID. Try to parse it. + IID iid; + RETURN_IF_FAILED(IIDFromString(value.GetRawBuffer(nullptr), &iid)); + + extensionMetadata.clsid = iid; + + vec.emplace_back(std::move(extensionMetadata)); + } + + return S_OK; +} + +[[nodiscard]] HRESULT DelegationConfig::s_GetAvailableConsoles(std::vector& consoles) noexcept +try +{ + return _lookupCatalog(DELEGATION_CONSOLE_EXTENSION_NAME, consoles); +} +CATCH_RETURN() + +[[nodiscard]] HRESULT DelegationConfig::s_GetAvailableTerminals(std::vector& terminals) noexcept +try +{ + return _lookupCatalog(DELEGATION_TERMINAL_EXTENSION_NAME, terminals); +} +CATCH_RETURN() + +[[nodiscard]] HRESULT DelegationConfig::s_SetConsole(const DelegationConsole& console) noexcept +{ + return s_Set(DELEGATION_CONSOLE_KEY_NAME, console.clsid); +} + +[[nodiscard]] HRESULT DelegationConfig::s_SetTerminal(const DelegationTerminal& terminal) noexcept +{ + return s_Set(DELEGATION_TERMINAL_KEY_NAME, terminal.clsid); +} + +[[nodiscard]] HRESULT DelegationConfig::s_GetConsole(IID& iid) noexcept +{ + return s_Get(DELEGATION_CONSOLE_KEY_NAME, iid); +} + +[[nodiscard]] HRESULT DelegationConfig::s_GetTerminal(IID& iid) noexcept +{ + return s_Get(DELEGATION_TERMINAL_KEY_NAME, iid); +} + +[[nodiscard]] HRESULT DelegationConfig::s_Get(PCWSTR value, IID& iid) noexcept +{ + wil::unique_hkey currentUserKey; + wil::unique_hkey consoleKey; + + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_OpenConsoleKey(¤tUserKey, &consoleKey)); + + wil::unique_hkey startupKey; + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_OpenKey(consoleKey.get(), L"%%Startup", &startupKey)); + + DWORD bytesNeeded = 0; + NTSTATUS result = RegistrySerialization::s_QueryValue(startupKey.get(), + value, + 0, + REG_SZ, + nullptr, + &bytesNeeded); + + if (NTSTATUS_FROM_WIN32(ERROR_SUCCESS) != result) + { + RETURN_NTSTATUS(result); + } + + auto buffer = std::make_unique(bytesNeeded / sizeof(wchar_t)); + + DWORD bytesUsed = 0; + + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_QueryValue(startupKey.get(), + value, + bytesNeeded, + REG_SZ, + reinterpret_cast(buffer.get()), + &bytesUsed)); + + RETURN_IF_FAILED(IIDFromString(buffer.get(), &iid)); + + return S_OK; +} + +[[nodiscard]] HRESULT DelegationConfig::s_Set(PCWSTR value, const CLSID clsid) noexcept +try +{ + wil::unique_hkey currentUserKey; + wil::unique_hkey consoleKey; + + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_OpenConsoleKey(¤tUserKey, &consoleKey)); + + wil::unique_hkey startupKey; + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_OpenKey(consoleKey.get(), L"%%Startup", &startupKey)); + + wil::unique_cotaskmem_string str; + RETURN_IF_FAILED(StringFromCLSID(clsid, &str)); + + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_SetValue(startupKey.get(), value, REG_SZ, reinterpret_cast(str.get()), gsl::narrow(wcslen(str.get() + 1) * sizeof(wchar_t)))); + + return S_OK; +} +CATCH_RETURN() diff --git a/src/propslib/DelegationConfig.hpp b/src/propslib/DelegationConfig.hpp new file mode 100644 index 00000000000..7136395945e --- /dev/null +++ b/src/propslib/DelegationConfig.hpp @@ -0,0 +1,48 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- DelegationConfig.hpp + +Abstract: +- This module is used for looking up delegation handlers for the launch of the default console hosting environment + +Author(s): +- Michael Niksa (MiNiksa) 31-Aug-2020 + +--*/ + +#pragma once + +class DelegationConfig +{ +public: + struct DelegationBase + { + CLSID clsid; + std::wstring name; + std::wstring author; + }; + + struct DelegationConsole : public DelegationBase + { + }; + + struct DelegationTerminal : public DelegationBase + { + }; + + [[nodiscard]] static HRESULT s_GetAvailableConsoles(std::vector& consoles) noexcept; + [[nodiscard]] static HRESULT s_GetAvailableTerminals(std::vector& terminals) noexcept; + + [[nodiscard]] static HRESULT s_SetConsole(const DelegationConsole& console) noexcept; + [[nodiscard]] static HRESULT s_SetTerminal(const DelegationTerminal& terminal) noexcept; + + [[nodiscard]] static HRESULT s_GetConsole(IID& iid) noexcept; + [[nodiscard]] static HRESULT s_GetTerminal(IID& iid) noexcept; + +private: + [[nodiscard]] static HRESULT s_Get(PCWSTR value, IID& iid) noexcept; + [[nodiscard]] static HRESULT s_Set(PCWSTR value, const CLSID clsid) noexcept; +}; diff --git a/src/propslib/propslib.vcxproj b/src/propslib/propslib.vcxproj index e1f4bf2837e..21ccacc258f 100644 --- a/src/propslib/propslib.vcxproj +++ b/src/propslib/propslib.vcxproj @@ -6,10 +6,11 @@ propslib PropertiesLibrary ConProps - StaticLibrary + StaticLibrary + @@ -19,6 +20,7 @@ + diff --git a/src/propslib/propslib.vcxproj.filters b/src/propslib/propslib.vcxproj.filters index e81f439a5f4..05074548724 100644 --- a/src/propslib/propslib.vcxproj.filters +++ b/src/propslib/propslib.vcxproj.filters @@ -27,6 +27,9 @@ Source Files + + Source Files + @@ -44,5 +47,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/src/propslib/sources b/src/propslib/sources index 07883865673..0c57c93630a 100644 --- a/src/propslib/sources +++ b/src/propslib/sources @@ -42,10 +42,12 @@ PRECOMPILED_CXX = 1 PRECOMPILED_INCLUDE = precomp.h SOURCES = \ + DelegationConfig.cpp \ ShortcutSerialization.cpp \ RegistrySerialization.cpp \ TrueTypeFontList.cpp \ INCLUDES = \ + $(ABI_INC_PATH)\; \ $(INCLUDES); \ ..\inc; \ diff --git a/src/server/ConDrvDeviceComm.cpp b/src/server/ConDrvDeviceComm.cpp index 2a43e13d01e..2598b3eadf4 100644 --- a/src/server/ConDrvDeviceComm.cpp +++ b/src/server/ConDrvDeviceComm.cpp @@ -173,3 +173,12 @@ ConDrvDeviceComm::~ConDrvDeviceComm() { return reinterpret_cast(handleId); } + +// Routine Description: +// - Provides acccess to the raw server handle so it can be used to hand off +// the session to another console host server. +[[nodiscard]] HRESULT ConDrvDeviceComm::GetServerHandle(_Out_ HANDLE* pHandle) const +{ + *pHandle = _Server.get(); + return S_OK; +} diff --git a/src/server/ConDrvDeviceComm.h b/src/server/ConDrvDeviceComm.h index 0178bcb43ca..392cc4e0fca 100644 --- a/src/server/ConDrvDeviceComm.h +++ b/src/server/ConDrvDeviceComm.h @@ -40,6 +40,8 @@ class ConDrvDeviceComm : public IDeviceComm [[nodiscard]] ULONG_PTR PutHandle(const void*) override; [[nodiscard]] void* GetHandle(ULONG_PTR) const override; + [[nodiscard]] HRESULT GetServerHandle(_Out_ HANDLE* pHandle) const override; + private: [[nodiscard]] HRESULT _CallIoctl(_In_ DWORD dwIoControlCode, _In_reads_bytes_opt_(cbInBufferSize) PVOID pInBuffer, diff --git a/src/server/DeviceComm.h b/src/server/DeviceComm.h index 88c34b97ba5..641177f66d8 100644 --- a/src/server/DeviceComm.h +++ b/src/server/DeviceComm.h @@ -35,4 +35,6 @@ class IDeviceComm [[nodiscard]] virtual ULONG_PTR PutHandle(const void*) = 0; [[nodiscard]] virtual void* GetHandle(ULONG_PTR) const = 0; + + [[nodiscard]] virtual HRESULT GetServerHandle(_Out_ HANDLE* pHandle) const = 0; }; diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index 7f6aff0ea10..26fb92b8607 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -16,7 +16,12 @@ #include "../interactivity/inc/ServiceLocator.hpp" +#include "../types/inc/utils.hpp" + +#include "IConsoleHandoff.h" + using namespace Microsoft::Console::Interactivity; +using namespace Microsoft::Console::Utils; // From ntstatus.h, which we cannot include without causing a bunch of other conflicts. So we just include the one code we need. // @@ -142,7 +147,8 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleCloseObject(_In_ PCONSOLE_API_MSG pMessag // - The response data to this request message. PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API_MSG pReceiveMsg) { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + Globals& Globals = ServiceLocator::LocateGlobals(); + CONSOLE_INFORMATION& gci = Globals.getConsoleInformation(); Telemetry::Instance().LogApiCall(Telemetry::ApiCall::AttachConsole); ConsoleProcessHandle* ProcessData = nullptr; @@ -159,6 +165,54 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API goto Error; } + + + // If we are NOT a PTY session (headless)... + // we have FOUND a CLSID for a different console to be the default startup handler... + // we are NOT already receiving an inbound console connection handoff... + // and the client app is going to end up showing a window... + // then attempt to delegate the startup to the registered replacement. + if (!Globals.launchArgs.IsHeadless() && Globals.handoffConsoleClsid && !Globals.handoffTarget && ConsoleConnectionDeservesVisibleWindow(&Cac)) + { + try + { + // Go get ourselves some COM. + auto coinit = wil::CoInitializeEx(COINIT_MULTITHREADED); + + // Get the class/interface to the handoff handler. Local machine only. + ::Microsoft::WRL::ComPtr handoff; + THROW_IF_FAILED(CoCreateInstance(Globals.handoffConsoleClsid.value(), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&handoff))); + + // Pack up just enough of the attach message for the other console to process it. + // NOTE: It can and will pick up the size/title/etc parameters from the driver again. + CONSOLE_PORTABLE_ATTACH_MSG msg{}; + msg.IdHighPart = pReceiveMsg->Descriptor.Identifier.HighPart; + msg.IdLowPart = pReceiveMsg->Descriptor.Identifier.LowPart; + msg.Process = pReceiveMsg->Descriptor.Process; + msg.Object = pReceiveMsg->Descriptor.Object; + msg.Function = pReceiveMsg->Descriptor.Function; + msg.InputSize = pReceiveMsg->Descriptor.InputSize; + msg.OutputSize = pReceiveMsg->Descriptor.OutputSize; + + // Attempt to get server handle out of our own communication stack to pass it on. + HANDLE serverHandle; + THROW_IF_FAILED(Globals.pDeviceComm->GetServerHandle(&serverHandle)); + + // Okay, moment of truth! If they say they successfully took it over, we're going to clean up. + // If they fail, we'll throw here and it'll log and we'll just start normally. + THROW_IF_FAILED(handoff->EstablishHandoff(serverHandle, + Globals.hInputEvent.get(), + &msg)); + + // Unlock in case anything tries to spool down as we exit. + UnlockConsole(); + + // We've handed off responsibility. Exit process to clean up any outstanding things we have open. + ExitProcess(S_OK); + } + CATCH_LOG(); // Just log, don't do anything more. We'll move on to launching normally on failure. + } + Status = NTSTATUS_FROM_HRESULT(gci.ProcessHandleList.AllocProcessData(dwProcessId, dwThreadId, Cac.ProcessGroupId, diff --git a/src/server/lib/server.vcxproj b/src/server/lib/server.vcxproj index 0db2f8f12e3..ec7ec05432c 100644 --- a/src/server/lib/server.vcxproj +++ b/src/server/lib/server.vcxproj @@ -57,6 +57,16 @@ + + + {e437b604-3e98-4f40-a927-e173e818ea4b} + + + + + $(IntDir)..\Host.ProxyDll;%(AdditionalIncludeDirectories) + + diff --git a/src/server/lib/server.vcxproj.filters b/src/server/lib/server.vcxproj.filters index a3d52dab5ff..a7fdc4dccce 100644 --- a/src/server/lib/server.vcxproj.filters +++ b/src/server/lib/server.vcxproj.filters @@ -72,6 +72,9 @@ Source Files + + Source Files + @@ -137,5 +140,11 @@ Header Files + + Header Files + + + + \ No newline at end of file diff --git a/src/server/sources.inc b/src/server/sources.inc index 5300b91a501..6cfbc695fa7 100644 --- a/src/server/sources.inc +++ b/src/server/sources.inc @@ -54,4 +54,5 @@ SOURCES= \ INCLUDES= \ $(INCLUDES); \ + $(WINCORE_OBJ_PATH)\console\open\src\host\proxy\$(O); \ ..; \