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
This commit is contained in:
parent
38da2ff185
commit
3822d5b662
|
@ -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}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
DIRS=exe \
|
||||
DIRS=proxy \
|
||||
exe \
|
||||
lib \
|
||||
ut_lib \
|
||||
ut_host \
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E437B604-3E98-4F40-A927-E173E818EA4B}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>host</RootNamespace>
|
||||
<ProjectName>Host.DLL</ProjectName>
|
||||
<TargetName>ConhostV2</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\main.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\interactivity\base\lib\InteractivityBase.vcxproj">
|
||||
<Project>{06ec74cb-9a12-429c-b551-8562ec964846}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\interactivity\win32\lib\win32.LIB.vcxproj">
|
||||
<Project>{06ec74cb-9a12-429c-b551-8532ec964726}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\propslib\propslib.vcxproj">
|
||||
<Project>{345fd5a4-b32b-4f29-bd1c-b033bd2c35cc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\gdi\lib\gdi.vcxproj">
|
||||
<Project>{1c959542-bac2-4e55-9a6d-13251914cbb9}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\server\lib\server.vcxproj">
|
||||
<Project>{18d09a24-8240-42d6-8cb6-236eee820262}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\terminal\adapter\lib\adapter.vcxproj">
|
||||
<Project>{dcf55140-ef6a-4736-a403-957e4f7430bb}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\terminal\parser\lib\parser.vcxproj">
|
||||
<Project>{3ae13314-1939-4dfa-9c14-38ca0834050c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\tsf\tsf.vcxproj">
|
||||
<Project>{2fd12fbb-1ddb-46d8-b818-1023c624caca}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\types\lib\types.vcxproj">
|
||||
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\lib\hostlib.vcxproj">
|
||||
<Project>{06ec74cb-9a12-429c-b551-8562ec954746}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ConhostV2.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Host.DLL.rc" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<EmbedManifest>
|
||||
</EmbedManifest>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<ModuleDefinitionFile>ConhostV2.def</ModuleDefinitionFile>
|
||||
<AllowIsolation>true</AllowIsolation>
|
||||
<AdditionalManifestDependencies>type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'</AdditionalManifestDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
|
@ -70,6 +70,10 @@ public:
|
|||
|
||||
ApiRoutines api;
|
||||
|
||||
bool handoffTarget = false;
|
||||
|
||||
std::optional<CLSID> handoffConsoleClsid;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
void EnableConptyModeForTests(std::unique_ptr<Microsoft::Console::Render::VtEngine> vtRenderEngine);
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E437B604-3E98-4F40-A927-E173E818EA4B}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>openconsoleproxy</RootNamespace>
|
||||
<ProjectName>OpenConsoleProxy</ProjectName>
|
||||
<TargetName>OpenConsoleProxy</TargetName>
|
||||
<ConfigurationType>Utility</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(IntDir)\IConsoleHandoff.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="IConsoleHandoff.idl">
|
||||
<!--
|
||||
In Razzle, IDL files generate %FileName%.h
|
||||
In Visual Studio, IDL files generate %FileName%_h.h
|
||||
Visual Studio is easier to override than Razzle.
|
||||
This has to be built in both the OS and outside, so we
|
||||
override the easier-to-override side to a uniform name.
|
||||
-->
|
||||
<HeaderFileName>IConsoleHandoff.h</HeaderFileName>
|
||||
<MinimumTargetSystem>NT100</MinimumTargetSystem>
|
||||
<OutputDirectory>$(IntDir)</OutputDirectory>
|
||||
</Midl>
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
|
@ -15,29 +15,16 @@
|
|||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\precomp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<ClInclude Include="IConsoleHandoff_h.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ConhostV2.def">
|
||||
<Midl Include="IConsoleHandoff.idl">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</Midl>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Host.DLL.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -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);
|
||||
};
|
|
@ -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); \
|
|
@ -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 \
|
||||
|
|
@ -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.launchArgs = *args;
|
||||
|
||||
Globals.uiOEMCP = GetOEMCP();
|
||||
Globals.uiWindowsCP = GetACP();
|
||||
|
||||
Globals.pFontDefaultList = new RenderFontDefaults();
|
||||
|
||||
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))
|
||||
{
|
||||
Globals.pDeviceComm = new ConDrvDeviceComm(Server);
|
||||
|
||||
Globals.launchArgs = *args;
|
||||
|
||||
Globals.uiOEMCP = GetOEMCP();
|
||||
Globals.uiWindowsCP = GetACP();
|
||||
|
||||
Globals.pFontDefaultList = new RenderFontDefaults();
|
||||
|
||||
FontInfoBase::s_SetFontDefaultList(Globals.pFontDefaultList);
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -44,4 +44,9 @@ namespace Microsoft::Console::Internal
|
|||
{
|
||||
[[nodiscard]] HRESULT TrySetDarkMode(HWND hwnd) noexcept;
|
||||
}
|
||||
|
||||
namespace DefaultApp
|
||||
{
|
||||
[[nodiscard]] HRESULT CheckDefaultAppPolicy(bool& isEnabled) noexcept;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "DelegationConfig.hpp"
|
||||
|
||||
#include "RegistrySerialization.hpp"
|
||||
|
||||
#include <wil/resource.h>
|
||||
#include <wil/winrt.h>
|
||||
#include <windows.foundation.collections.h>
|
||||
#include <Windows.ApplicationModel.h>
|
||||
#include <Windows.ApplicationModel.AppExtensions.h>
|
||||
|
||||
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<typename T, typename std::enable_if<std::is_base_of<DelegationConfig::DelegationBase, T>::value>::type* = nullptr>
|
||||
HRESULT _lookupCatalog(PCWSTR extensionName, std::vector<T>& vec) noexcept
|
||||
{
|
||||
vec.clear();
|
||||
|
||||
auto coinit = wil::CoInitializeEx(COINIT_MULTITHREADED);
|
||||
|
||||
ComPtr<IAppExtensionCatalogStatics> catalogStatics;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_AppExtensions_AppExtensionCatalog).Get(), &catalogStatics));
|
||||
|
||||
ComPtr<IAppExtensionCatalog> catalog;
|
||||
RETURN_IF_FAILED(catalogStatics->Open(HStringReference(extensionName).Get(), &catalog));
|
||||
|
||||
ComPtr<IAsyncOperation<IVectorView<AppExtension*>*>> findOperation;
|
||||
RETURN_IF_FAILED(catalog->FindAllAsync(&findOperation));
|
||||
|
||||
ComPtr<IVectorView<AppExtension*>> 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<IAppExtension> extension;
|
||||
RETURN_IF_FAILED(extensionList->GetAt(index, &extension));
|
||||
|
||||
ComPtr<IPackage> extensionPackage;
|
||||
RETURN_IF_FAILED(extension->get_Package(&extensionPackage));
|
||||
|
||||
ComPtr<IPackageId> 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<IAsyncOperation<IPropertySet*>> propertiesOperation;
|
||||
RETURN_IF_FAILED(extension->GetExtensionPropertiesAsync(&propertiesOperation));
|
||||
|
||||
// Wait for async to complete and return the property set.
|
||||
ComPtr<IPropertySet> 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<IMap<HSTRING, IInspectable*>> map;
|
||||
RETURN_IF_FAILED(properties.As(&map));
|
||||
|
||||
// Looking it up is going to get us an inspectable
|
||||
ComPtr<IInspectable> 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<IPropertySet> anotherSet;
|
||||
RETURN_IF_FAILED(inspectable.As(&anotherSet));
|
||||
|
||||
// And we can't look at sets directly, so move it to map.
|
||||
ComPtr<IMap<HSTRING, IInspectable*>> 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<IInspectable> 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<IPropertyValue> 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<DelegationConsole>& consoles) noexcept
|
||||
try
|
||||
{
|
||||
return _lookupCatalog(DELEGATION_CONSOLE_EXTENSION_NAME, consoles);
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
[[nodiscard]] HRESULT DelegationConfig::s_GetAvailableTerminals(std::vector<DelegationTerminal>& 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<wchar_t[]>(bytesNeeded / sizeof(wchar_t));
|
||||
|
||||
DWORD bytesUsed = 0;
|
||||
|
||||
RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_QueryValue(startupKey.get(),
|
||||
value,
|
||||
bytesNeeded,
|
||||
REG_SZ,
|
||||
reinterpret_cast<BYTE*>(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<BYTE*>(str.get()), gsl::narrow<DWORD>(wcslen(str.get() + 1) * sizeof(wchar_t))));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
|
@ -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<DelegationConsole>& consoles) noexcept;
|
||||
[[nodiscard]] static HRESULT s_GetAvailableTerminals(std::vector<DelegationTerminal>& 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;
|
||||
};
|
|
@ -6,10 +6,11 @@
|
|||
<RootNamespace>propslib</RootNamespace>
|
||||
<ProjectName>PropertiesLibrary</ProjectName>
|
||||
<TargetName>ConProps</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DelegationConfig.cpp" />
|
||||
<ClCompile Include="RegistrySerialization.cpp" />
|
||||
<ClCompile Include="ShortcutSerialization.cpp" />
|
||||
<ClCompile Include="TrueTypeFontList.cpp" />
|
||||
|
@ -19,6 +20,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="conpropsp.hpp" />
|
||||
<ClInclude Include="DelegationConfig.hpp" />
|
||||
<ClInclude Include="RegistrySerialization.hpp" />
|
||||
<ClInclude Include="ShortcutSerialization.hpp" />
|
||||
<ClInclude Include="TrueTypeFontList.hpp" />
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
<ClCompile Include="precomp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DelegationConfig.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="conpropsp.hpp">
|
||||
|
@ -44,5 +47,8 @@
|
|||
<ClInclude Include="TrueTypeFontList.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DelegationConfig.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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; \
|
||||
|
|
|
@ -173,3 +173,12 @@ ConDrvDeviceComm::~ConDrvDeviceComm()
|
|||
{
|
||||
return reinterpret_cast<void*>(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;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ public:
|
|||
[[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,
|
||||
|
|
|
@ -35,4 +35,6 @@ public:
|
|||
|
||||
[[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;
|
||||
};
|
||||
|
|
|
@ -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<IConsoleHandoff> 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,
|
||||
|
|
|
@ -57,6 +57,16 @@
|
|||
<ClInclude Include="..\WaitTerminationReason.h" />
|
||||
<ClInclude Include="..\WinNTControl.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\host\dll\Host.DLL.vcxproj">
|
||||
<Project>{e437b604-3e98-4f40-a927-e173e818ea4b}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(IntDir)..\Host.ProxyDll;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
||||
|
|
|
@ -72,6 +72,9 @@
|
|||
<ClCompile Include="..\ProcessPolicy.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ConsoleShimPolicy.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h">
|
||||
|
@ -137,5 +140,11 @@
|
|||
<ClInclude Include="..\ProcessPolicy.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ConsoleShimPolicy.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -54,4 +54,5 @@ SOURCES= \
|
|||
|
||||
INCLUDES= \
|
||||
$(INCLUDES); \
|
||||
$(WINCORE_OBJ_PATH)\console\open\src\host\proxy\$(O); \
|
||||
..; \
|
||||
|
|
Loading…
Reference in New Issue