Implement Default Terminal (#7489)

- 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
This commit is contained in:
Michael Niksa 2021-03-26 15:09:49 -07:00 committed by GitHub
parent 690fbbbc27
commit 906edf7002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1483 additions and 423 deletions

View File

@ -11,6 +11,7 @@ CYICON
D2DERR_SHADER_COMPILE_FAILED
dataobject
DERR
dlldata
environstrings
EXPCMDFLAGS
EXPCMDSTATE
@ -83,6 +84,7 @@ schandle
semver
serializer
shobjidl
SINGLEUSE
SHOWMINIMIZED
SIZENS
smoothstep
@ -93,6 +95,8 @@ spsc
sregex
STDCPP
strchr
STDMETHOD
Stubless
Subheader
Subpage
UPDATEINIFILE
@ -108,8 +112,10 @@ UPDATEINIFILE
userenv
wcsstr
wcstoui
winmain
wpc
wsregex
wwinmain
XDocument
XElement
xlocmes

View File

@ -433,6 +433,7 @@ cstring
cstyle
csv
CSwitch
CTerminal
CText
ctime
ctl
@ -571,6 +572,7 @@ defaultsettings
DEFAULTTONEAREST
DEFAULTTONULL
DEFAULTTOPRIMARY
DEFCON
defectdefs
DEFERERASE
deff

17
.vscode/tasks.json vendored
View File

@ -16,7 +16,9 @@
"${workspaceFolder}\\OpenConsole.sln",
"/p:Configuration=${input:configChoice}",
"/p:Platform=${input:platformChoice}",
"/p:AppxSymbolPackageEnabled=false", // This takes a long time, so false if we don't really need it.
"/t:$target",
"/m", // Parallel builds
"/verbosity:minimal"
],
"problemMatcher": ["$msCompile"],
@ -46,8 +48,7 @@
],
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
"isDefault": true
"kind": "build"
}
},
{
@ -57,6 +58,18 @@
"args": [
],
"problemMatcher": ["$msCompile"],
},
{
"type": "process",
"label": "Run Code Format",
"command": "powershell.exe",
"args": [
"-Command",
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
"Set-MsBuildDevEnvironment;",
"Invoke-CodeFormat",
],
"problemMatcher": ["$msCompile"],
}
],
"inputs":[

View File

@ -12,7 +12,9 @@ Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "CascadiaPackage", "src\casc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.EXE", "src\host\exe\Host.EXE.vcxproj", "{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}"
ProjectSection(ProjectDependencies) = postProject
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
{5D23E8E1-3C64-4CC1-A8F7-6861677F7239} = {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PropertiesLibrary", "src\propslib\propslib.vcxproj", "{345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}"
@ -64,6 +66,7 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host", "src\host\lib\hostlib.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954746}"
ProjectSection(ProjectDependencies) = postProject
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
EndProjectSection
EndProject
@ -104,6 +107,9 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Build Common", "_Build Common", "{04170EEF-983A-4195-BFEF-2321E5E38A1E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "src\server\lib\server.vcxproj", "{18D09A24-8240-42D6-8CB6-236EEE820262}"
ProjectSection(ProjectDependencies) = postProject
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Host.Tests.UIA", "src\host\ft_uia\Host.Tests.UIA.csproj", "{C17E1BF3-9D34-4779-9458-A8EF98CC5662}"
ProjectSection(ProjectDependencies) = postProject
@ -369,7 +375,7 @@ 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}") = "OpenConsoleProxy", "src\host\proxy\Host.Proxy.vcxproj", "{E437B604-3E98-4F40-A927-E173E818EA4B}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenConsoleProxy", "src\host\proxy\Host.Proxy.vcxproj", "{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Window", "Window", "{2D17E75D-2DDC-42C4-AD70-704D95A937AE}"
EndProject
@ -2580,36 +2586,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
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x64.ActiveCfg = AuditMode|x64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x64.Build.0 = AuditMode|x64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x86.Build.0 = AuditMode|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|Any CPU.ActiveCfg = Debug|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|ARM.ActiveCfg = Debug|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|ARM64.ActiveCfg = Debug|ARM64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|ARM64.Build.0 = Debug|ARM64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x64.ActiveCfg = Debug|x64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x64.Build.0 = Debug|x64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x86.ActiveCfg = Debug|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x86.Build.0 = Debug|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|Any CPU.ActiveCfg = Release|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|ARM.ActiveCfg = Release|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|ARM64.ActiveCfg = Release|ARM64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|ARM64.Build.0 = Release|ARM64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x64.ActiveCfg = Release|x64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x64.Build.0 = Release|x64
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x86.ActiveCfg = Release|Win32
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2701,7 +2707,7 @@ Global
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE}
{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}
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{2D17E75D-2DDC-42C4-AD70-704D95A937AE} = {59840756-302F-44DF-AA47-441A9D673202}
{77875138-BB08-49F9-8BB1-409C2150E0E1} = {59840756-302F-44DF-AA47-441A9D673202}
{9921CA0A-320C-4460-8623-3A3196E7F4CB} = {59840756-302F-44DF-AA47-441A9D673202}

View File

@ -70,6 +70,7 @@
<ItemGroup>
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
<ProjectReference Include="..\..\host\proxy\Host.Proxy.vcxproj" />
<ProjectReference Include="..\TerminalAzBridge\TerminalAzBridge.vcxproj" />
<ProjectReference Include="..\ShellExtension\WindowsTerminalShellExt.vcxproj" />
<ProjectReference Include="..\wt\wt.vcxproj" />

View File

@ -74,9 +74,43 @@
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole-Dev"
DisplayName="OpenConsole Dev"
Description="Console host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{1F9F2BF5-5BC3-4F17-B0E6-912413F1F451}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
Id="Terminal-Dev"
DisplayName="Windows Terminal Dev"
Description="Terminal host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{051F34EE-C1FD-4B19-AF75-9BA54648434C}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<com:Extension Category="windows.comInterface">
<com:ComInterface>
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="1F9F2BF5-5BC3-4F17-B0E6-912413F1F451"/>
</com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
<com:Class Id="051F34EE-C1FD-4B19-AF75-9BA54648434C"/>
</com:ExeServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="52065414-e077-47ec-a3ac-1cc5455e1b54" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>

View File

@ -75,8 +75,43 @@
Enabled="false"
DisplayName="ms-resource:AppNamePre" />
</uap5:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole-Pre"
DisplayName="OpenConsole Preview"
Description="Console host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{06EC847C-C0A5-46B8-92CB-7C92F6E35CD5}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
Id="Terminal-Pre"
DisplayName="Windows Terminal Preview"
Description="Terminal host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{86633F1F-6454-40EC-89CE-DA4EBA977EE2}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<com:Extension Category="windows.comInterface">
<com:ComInterface>
<com:ProxyStub Id="1833E661-CC81-4DD0-87C6-C2F74BD39EFA" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="06EC847C-C0A5-46B8-92CB-7C92F6E35CD5"/>
</com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe"
<com:Class Id="86633F1F-6454-40EC-89CE-DA4EBA977EE2"/>
</com:ExeServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="02db545a-3e20-46de-83a5-1329b1e88b6b" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>

View File

@ -75,9 +75,43 @@
Enabled="false"
DisplayName="ms-resource:AppName" />
</uap5:Extension>
<!-- <uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole"
DisplayName="OpenConsole"
Description="Console host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
Id="Terminal"
DisplayName="Windows Terminal"
Description="Terminal host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{E12CFF52-A866-4C77-9A90-F570A7AA2C6B}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<com:Extension Category="windows.comInterface">
<com:ComInterface>
<com:ProxyStub Id="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
</com:ComInterface>
</com:Extension> -->
<com:Extension Category="windows.comServer">
<com:ComServer>
<!-- <com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"/>
</com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
<com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/>
</com:ExeServer> -->
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>

View File

@ -684,6 +684,18 @@ std::vector<ActionAndArgs>& AppCommandlineArgs::GetStartupActions()
return _startupActions;
}
// Method Description:
// - Returns whether we should start listening for inbound PTY connections
// coming from the operating system default application feature.
// Arguments:
// - <none>
// Return Value:
// - True if the listener should be started. False otherwise.
bool AppCommandlineArgs::IsHandoffListener() const noexcept
{
return _isHandoffListener;
}
// Method Description:
// - Get the string of text that should be displayed to the user on exit. This
// is usually helpful for cases where the user entered some sort of invalid
@ -726,17 +738,23 @@ bool AppCommandlineArgs::ShouldExitEarly() const noexcept
// - <none>
void AppCommandlineArgs::ValidateStartupCommands()
{
// If we parsed no commands, or the first command we've parsed is not a new
// tab action, prepend a new-tab command to the front of the list.
if (_startupActions.empty() ||
_startupActions.front().Action() != ShortcutAction::NewTab)
// Only check over the actions list for the potential to add a new-tab
// command if we are not starting for the purposes of receiving an inbound
// handoff connection from the operating system.
if (!_isHandoffListener)
{
// Build the NewTab action from the values we've parsed on the commandline.
NewTerminalArgs newTerminalArgs{};
NewTabArgs args{ newTerminalArgs };
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
// push the arg onto the front
_startupActions.insert(_startupActions.begin(), 1, newTabAction);
// If we parsed no commands, or the first command we've parsed is not a new
// tab action, prepend a new-tab command to the front of the list.
if (_startupActions.empty() ||
_startupActions.front().Action() != ShortcutAction::NewTab)
{
// Build the NewTab action from the values we've parsed on the commandline.
NewTerminalArgs newTerminalArgs{};
NewTabArgs args{ newTerminalArgs };
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
// push the arg onto the front
_startupActions.insert(_startupActions.begin(), 1, newTabAction);
}
}
}
@ -760,6 +778,15 @@ std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> AppComman
// - 0 if the commandline was successfully parsed
int AppCommandlineArgs::ParseArgs(winrt::array_view<const winrt::hstring>& args)
{
for (const auto& arg : args)
{
if (arg == L"-Embedding")
{
_isHandoffListener = true;
return 0;
}
}
auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args);
for (auto& cmdBlob : commands)
@ -864,6 +891,7 @@ void AppCommandlineArgs::FullResetState()
_startupActions.clear();
_exitMessage = "";
_shouldExitEarly = false;
_isHandoffListener = false;
_windowTarget = {};
}

View File

@ -35,6 +35,7 @@ public:
void ValidateStartupCommands();
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs>& GetStartupActions();
bool IsHandoffListener() const noexcept;
const std::string& GetExitMessage();
bool ShouldExitEarly() const noexcept;
@ -104,6 +105,7 @@ private:
const Commandline* _currentCommandline{ nullptr };
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> _launchMode{ std::nullopt };
bool _isHandoffListener{ false };
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
std::string _exitMessage;
bool _shouldExitEarly{ false };

View File

@ -1166,6 +1166,17 @@ namespace winrt::TerminalApp::implementation
_hasCommandLineArguments = args.size() > 1;
_appArgs.ValidateStartupCommands();
_root->SetStartupActions(_appArgs.GetStartupActions());
// Check if we were started as a COM server for inbound connections of console sessions
// coming out of the operating system default application feature. If so,
// tell TerminalPage to start the listener as we have to make sure it has the chance
// to register a handler to hear about the requests first and is all ready to receive
// them before the COM server registers itself. Otherwise, the request might come
// in and be routed to an event with no handlers or a non-ready Page.
if (_appArgs.IsHandoffListener())
{
_root->SetInboundListener();
}
}
return result;

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->

View File

@ -194,6 +194,9 @@ namespace winrt::TerminalApp::implementation
// Hookup our event handlers to the ShortcutActionDispatch
_RegisterActionCallbacks();
// Hook up inbound connection event handler
TerminalConnection::ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
//Event Bindings (Early)
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
@ -351,15 +354,29 @@ namespace winrt::TerminalApp::implementation
if (_startupState == StartupState::NotInitialized)
{
_startupState = StartupState::InStartup;
if (_startupActions.Size() == 0)
{
_OpenNewTab(nullptr);
ProcessStartupActions(_startupActions, true);
_CompleteInitialization();
}
else
// If we were told that the COM server needs to be started to listen for incoming
// default application connections, start it now.
// 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)
{
ProcessStartupActions(_startupActions, true);
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()
}
}
}
@ -382,11 +399,6 @@ namespace winrt::TerminalApp::implementation
const bool initial,
const winrt::hstring cwd)
{
// If there are no actions left, do nothing.
if (actions.Size() == 0)
{
return;
}
auto weakThis{ get_weak() };
// Handle it on a subsequent pass of the UI thread.
@ -757,13 +769,16 @@ namespace winrt::TerminalApp::implementation
// - newTerminalArgs: An object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See TerminalSettings::CreateWithNewTerminalArgs for more details.
void TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs)
// - existingConnection: An optional connection that is already established to a PTY
// for this tab to host instead of creating one.
// If not defined, the tab will create the connection.
void TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
try
{
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
_CreateNewTabFromSettings(profileGuid, settings);
_CreateNewTabFromSettings(profileGuid, settings, existingConnection);
const uint32_t tabCount = _tabs.Size();
const bool usedManualProfile = (newTerminalArgs != nullptr) &&
@ -804,13 +819,14 @@ namespace winrt::TerminalApp::implementation
// - Creates a new tab with the given settings. If the tab bar is not being
// currently displayed, it will be shown.
// Arguments:
// - profileGuid: ID to use to lookup profile settings for this connection
// - settings: the TerminalSettings object to use to create the TerminalControl with.
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings)
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings, TerminalConnection::ITerminalConnection existingConnection)
{
// Initialize the new tab
// Create a connection based on the values in our settings object.
auto connection = _CreateConnectionFromSettings(profileGuid, settings);
// Create a connection based on the values in our settings object if we weren't given one.
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings);
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
if (_settings.GlobalSettings().DebugFeaturesEnabled())
@ -2723,6 +2739,19 @@ namespace winrt::TerminalApp::implementation
_startupActions = winrt::single_threaded_vector<ActionAndArgs>(std::move(listCopy));
}
// Routine Description:
// - Notifies this Terminal Page that it should start the incoming connection
// listener for command-line tools attempting to join this Terminal
// through the default application channel.
// Arguments:
// - <none> - Implicitly sets to true. Default page state is false.
// Return Value:
// - <none>
void TerminalPage::SetInboundListener()
{
_shouldStartInboundListener = true;
}
winrt::TerminalApp::IDialogPresenter TerminalPage::DialogPresenter() const
{
return _dialogPresenter.get();
@ -3033,6 +3062,7 @@ namespace winrt::TerminalApp::implementation
{
return _isFullscreen;
}
// Method Description:
// - Returns true if we're currently in "Always on top" mode. When we're in
// always on top mode, the window should be on top of all other windows.
@ -3047,6 +3077,12 @@ namespace winrt::TerminalApp::implementation
return _isAlwaysOnTop;
}
void TerminalPage::_OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection)
{
// TODO: GH 9458 will give us more context so we can try to choose a better profile.
_OpenNewTab(nullptr, connection);
}
// Method Description:
// - Updates all tabs with their current index in _tabs.
// Arguments:
@ -3155,7 +3191,6 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// Method Description:
// - Computes the delta for scrolling the tab's viewport.
// Arguments:

View File

@ -66,6 +66,7 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const;
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
void SetInboundListener();
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
@ -138,6 +139,7 @@ namespace winrt::TerminalApp::implementation
StartupState _startupState{ StartupState::NotInitialized };
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
bool _shouldStartInboundListener{ false };
void _ShowAboutDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseWarningDialog();
@ -147,8 +149,8 @@ namespace winrt::TerminalApp::implementation
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
void _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs);
void _CreateNewTabFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
void _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
void _CreateNewTabFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
@ -275,6 +277,8 @@ namespace winrt::TerminalApp::implementation
void _HidePointerCursorHandler(const IInspectable& sender, const IInspectable& eventArgs);
void _RestorePointerCursorHandler(const IInspectable& sender, const IInspectable& eventArgs);
void _OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
void _HandleOpenNewTabDropdown(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
@ -322,6 +326,7 @@ namespace winrt::TerminalApp::implementation
void _HandleFindMatch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleTogglePaneReadOnly(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleNewWindow(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleToggleInboundPty(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
// Make sure to hook new actions up in _RegisterActionCallbacks!
#pragma endregion

View File

@ -0,0 +1,108 @@
// 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);
}

View File

@ -0,0 +1,56 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CTerminalHandoff.h
Abstract:
- This module receives an incoming request to host a terminal UX
for a console mode application already started and attached to a PTY.
Author(s):
- Michael Niksa (MiNiksa) 31-Aug-2020
--*/
#pragma once
#include "ITerminalHandoff.h"
#if defined(WT_BRANDING_RELEASE)
#define __CLSID_CTerminalHandoff "E12CFF52-A866-4C77-9A90-F570A7AA2C6B"
#elif defined(WT_BRANDING_PREVIEW)
#define __CLSID_CTerminalHandoff "86633F1F-6454-40EC-89CE-DA4EBA977EE2"
#else
#define __CLSID_CTerminalHandoff "051F34EE-C1FD-4B19-AF75-9BA54648434C"
#endif
using NewHandoffFunction = HRESULT (*)(HANDLE, HANDLE, HANDLE, HANDLE);
struct __declspec(uuid(__CLSID_CTerminalHandoff))
CTerminalHandoff : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, ITerminalHandoff>
{
#pragma region ITerminalHandoff
STDMETHODIMP EstablishPtyHandoff(HANDLE in,
HANDLE out,
HANDLE signal,
HANDLE process) noexcept override;
#pragma endregion
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff) noexcept;
static HRESULT s_StopListening() noexcept;
};
// Disable warnings from the CoCreatableClass macro as the value it provides for
// automatic COM class registration is of much greater value than the nits from
// the static analysis warnings.
#pragma warning(push)
#pragma warning(disable : 26477) // Macro uses 0/NULL over nullptr.
#pragma warning(disable : 26476) // Macro uses naked union over variant.
CoCreatableClass(CTerminalHandoff);
#pragma warning(pop)

View File

@ -9,6 +9,7 @@
#include <userenv.h>
#include "ConptyConnection.g.cpp"
#include "CTerminalHandoff.h"
#include "../../types/inc/utils.hpp"
#include "../../types/inc/Environment.hpp"
@ -190,6 +191,32 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
CATCH_RETURN();
ConptyConnection::ConptyConnection(const HANDLE hSig,
const HANDLE hIn,
const HANDLE hOut,
const HANDLE hClientProcess) :
_initialRows{ 25 },
_initialCols{ 80 },
_commandline{ L"" },
_startingDirectory{ L"" },
_startingTitle{ L"" },
_environment{ nullptr },
_guid{},
_u8State{},
_u16Str{},
_buffer{},
_inPipe{ hIn },
_outPipe{ hOut }
{
hSig; // TODO: GH 9464 this needs to be packed into the hpcon
if (_guid == guid{})
{
_guid = Utils::CreateGuid();
}
_piClient.hProcess = hClientProcess;
}
ConptyConnection::ConptyConnection(const hstring& commandline,
const hstring& startingDirectory,
const hstring& startingTitle,
@ -222,9 +249,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void ConptyConnection::Start()
try
{
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, PSEUDOCONSOLE_RESIZE_QUIRK | PSEUDOCONSOLE_WIN32_INPUT_MODE, &_inPipe, &_outPipe, &_hPC));
THROW_IF_FAILED(_LaunchAttachedClient());
if (!_inPipe)
{
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, PSEUDOCONSOLE_RESIZE_QUIRK | PSEUDOCONSOLE_WIN32_INPUT_MODE, &_inPipe, &_outPipe, &_hPC));
THROW_IF_FAILED(_LaunchAttachedClient());
}
_startTime = std::chrono::high_resolution_clock::now();
@ -451,6 +481,31 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return 0;
}
static winrt::event<NewConnectionHandler> _newConnectionHandlers;
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
try
{
auto conn = winrt::make<implementation::ConptyConnection>(signal, in, out, process);
_newConnectionHandlers(conn);
return S_OK;
}
CATCH_RETURN()
void ConptyConnection::StartInboundListener()
{
THROW_IF_FAILED(CTerminalHandoff::s_StartListening(&ConptyConnection::NewHandoff));
}
void ConptyConnection::StopInboundListener()
{
THROW_IF_FAILED(CTerminalHandoff::s_StopListening());
}
// Function Description:
// - This function will be called (by C++/WinRT) after the final outstanding reference to
// any given connection instance is released.

View File

@ -19,6 +19,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
struct ConptyConnection : ConptyConnectionT<ConptyConnection>, ConnectionStateHolder<ConptyConnection>
{
ConptyConnection(const HANDLE hSig,
const HANDLE hIn,
const HANDLE hOut,
const HANDLE hClientProcess);
ConptyConnection(
const hstring& cmdline,
const hstring& startingDirectory,
@ -36,6 +41,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
winrt::guid Guid() const noexcept;
static void StartInboundListener();
static void StopInboundListener();
static winrt::event_token NewConnection(NewConnectionHandler const& handler);
static void NewConnection(winrt::event_token const& token);
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
private:
@ -43,6 +54,8 @@ 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;
uint32_t _initialRows{};
uint32_t _initialCols{};
hstring _commandline;

View File

@ -5,11 +5,13 @@ import "ITerminalConnection.idl";
namespace Microsoft.Terminal.TerminalConnection
{
[default_interface]
runtimeclass ConptyConnection : ITerminalConnection
[default_interface] runtimeclass ConptyConnection : ITerminalConnection
{
ConptyConnection(String cmdline, String startingDirectory, String startingTitle, IMapView<String,String> environment, UInt32 rows, UInt32 columns, Guid guid);
ConptyConnection(String cmdline, String startingDirectory, String startingTitle, IMapView<String, String> environment, UInt32 rows, UInt32 columns, Guid guid);
Guid Guid { get; };
};
static event NewConnectionHandler NewConnection;
static void StartInboundListener();
static void StopInboundListener();
};
}

View File

@ -27,4 +27,6 @@ namespace Microsoft.Terminal.TerminalConnection
event Windows.Foundation.TypedEventHandler<ITerminalConnection, Object> StateChanged;
ConnectionState State { get; };
};
delegate void NewConnectionHandler(ITerminalConnection connection);
}

View File

@ -15,6 +15,7 @@
<ClInclude Include="AzureConnection.h">
<DependentUpon>AzureConnection.idl</DependentUpon>
</ClInclude>
<ClInclude Include="CTerminalHandoff.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="ConptyConnection.h">
<DependentUpon>ConptyConnection.idl</DependentUpon>
@ -24,6 +25,7 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CTerminalHandoff.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="AzureConnection.cpp">
<DependentUpon>AzureConnection.idl</DependentUpon>
@ -77,9 +79,12 @@
<Error Condition="!Exists('..\..\..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets'))" />
</Target>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>$(OpenConsoleCommonOutDir)\conptylib.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>

View File

@ -18,12 +18,14 @@
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="AzureConnection.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="CTerminalHandoff.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="EchoConnection.h" />
<ClInclude Include="AzureConnection.h" />
<ClInclude Include="AzureClientID.h" />
<ClInclude Include="CTerminalHandoff.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="ITerminalConnection.idl" />
@ -38,8 +40,6 @@
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/en-US/Resources.resw">
<Filter>Resources\en-US</Filter>
</PRIResource>
<PRIResource Include="Resources\en-US\Resources.resw" />
</ItemGroup>
</Project>

View File

@ -37,4 +37,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto profile{ winrt::unbox_value<Model::Profile>(value) };
_State.Settings().GlobalSettings().DefaultProfile(profile.Guid());
}
// TODO GH#9463 - Complete hookup of Terminal UX to choose defapp.
Windows::Foundation::Collections::IObservableVector<IInspectable> Launch::DefaultTerminals()
{
Windows::Foundation::Collections::IObservableVector<IInspectable> vec;
return vec;
}
IInspectable Launch::CurrentDefaultTerminal()
{
return nullptr;
}
void Launch::CurrentDefaultTerminal(const IInspectable& value)
{
value;
}
}

View File

@ -28,6 +28,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
IInspectable CurrentDefaultProfile();
void CurrentDefaultProfile(const IInspectable& value);
Windows::Foundation::Collections::IObservableVector<IInspectable> DefaultTerminals();
IInspectable CurrentDefaultTerminal();
void CurrentDefaultTerminal(const IInspectable& value);
WINRT_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(LaunchMode, Model::LaunchMode, State().Settings().GlobalSettings, LaunchMode);

View File

@ -17,10 +17,12 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentDefaultProfile;
IInspectable CurrentDefaultTerminal;
IObservableVector<IInspectable> DefaultTerminals { get; };
IInspectable CurrentLaunchMode;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };
IInspectable CurrentWindowingBehavior;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> WindowingBehaviorList { get; };
}

View File

@ -67,6 +67,16 @@ the MIT License. See LICENSE in the project root for license information. -->
</ComboBox>
</local:SettingContainer>
<!-- Default Terminal -->
<local:SettingContainer x:Uid="Globals_DefaultTerminal">
<ComboBox x:Name="DefaultTerminal"
x:Load="False"
ItemsSource="{x:Bind DefaultTerminals, Mode=OneWay}"
SelectedItem="{x:Bind CurrentDefaultTerminal, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
</ComboBox>
</local:SettingContainer>
<!--Start on User Login-->
<local:SettingContainer x:Uid="Globals_StartOnUserLogin">
<ToggleSwitch IsOn="{x:Bind State.Settings.GlobalSettings.StartOnUserLogin, Mode=TwoWay}"/>

View File

@ -221,6 +221,14 @@
<value>Profile that opens when clicking the '+' icon or by typing the new tab key binding.</value>
<comment>A description for what the default profile is and when it's used.</comment>
</data>
<data name="Globals_DefaultTerminal.Header" xml:space="preserve">
<value>Default terminal</value>
<comment>Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned.</comment>
</data>
<data name="Globals_DefaultTerminal.HelpText" xml:space="preserve">
<value>The terminal application that launches when a command-line application is run without an existing session, like from the Start Menu or Run dialog.</value>
<comment>A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window.</comment>
</data>
<data name="Globals_ForceFullRepaint.Header" xml:space="preserve">
<value>Redraw entire screen when display updates</value>
<comment>Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames.</comment>

View File

@ -22,6 +22,7 @@ const std::wstring_view ConsoleArguments::RESIZE_QUIRK = L"--resizeQuirk";
const std::wstring_view ConsoleArguments::WIN32_INPUT_MODE = L"--win32input";
const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature";
const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty";
const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding";
std::wstring EscapeArgument(std::wstring_view ac)
{
@ -109,6 +110,7 @@ ConsoleArguments::ConsoleArguments(const std::wstring& commandline,
_clientCommandline = L"";
_vtMode = L"";
_headless = false;
_runAsComServer = false;
_createServerHandle = true;
_serverHandle = 0;
_signalHandle = 0;
@ -141,6 +143,7 @@ ConsoleArguments& ConsoleArguments::operator=(const ConsoleArguments& other)
_height = other._height;
_inheritCursor = other._inheritCursor;
_receivedEarlySizeChange = other._receivedEarlySizeChange;
_runAsComServer = other._runAsComServer;
}
return *this;
@ -446,6 +449,12 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector<std::wstring>& args, _In
s_ConsumeArg(args, i);
hr = S_OK;
}
else if (arg == COM_SERVER_ARG)
{
_runAsComServer = true;
s_ConsumeArg(args, i);
hr = S_OK;
}
else if (arg.substr(0, FILEPATH_LEADER_PREFIX.length()) == FILEPATH_LEADER_PREFIX)
{
// beginning of command line -- includes file path
@ -576,6 +585,11 @@ bool ConsoleArguments::ShouldCreateServerHandle() const
return _createServerHandle;
}
bool ConsoleArguments::ShouldRunAsComServer() const
{
return _runAsComServer;
}
HANDLE ConsoleArguments::GetServerHandle() const
{
return ULongToHandle(_serverHandle);
@ -596,6 +610,11 @@ HANDLE ConsoleArguments::GetVtOutHandle() const
return _vtOutHandle;
}
std::wstring ConsoleArguments::GetOriginalCommandLine() const
{
return _commandline;
}
std::wstring ConsoleArguments::GetClientCommandline() const
{
return _clientCommandline;

View File

@ -35,6 +35,7 @@ public:
bool InConptyMode() const noexcept;
bool IsHeadless() const;
bool ShouldCreateServerHandle() const;
bool ShouldRunAsComServer() const;
HANDLE GetServerHandle() const;
HANDLE GetVtInHandle() const;
@ -43,6 +44,7 @@ public:
bool HasSignalHandle() const;
HANDLE GetSignalHandle() const;
std::wstring GetOriginalCommandLine() const;
std::wstring GetClientCommandline() const;
std::wstring GetVtMode() const;
bool GetForceV1() const;
@ -74,6 +76,7 @@ public:
static const std::wstring_view WIN32_INPUT_MODE;
static const std::wstring_view FEATURE_ARG;
static const std::wstring_view FEATURE_PTY_ARG;
static const std::wstring_view COM_SERVER_ARG;
private:
#ifdef UNIT_TESTING
@ -90,7 +93,8 @@ private:
const bool createServerHandle,
const DWORD serverHandle,
const DWORD signalHandle,
const bool inheritCursor) :
const bool inheritCursor,
const bool runAsComServer) :
_commandline(commandline),
_clientCommandline(clientCommandline),
_vtInHandle(vtInHandle),
@ -107,7 +111,8 @@ private:
_resizeQuirk(false),
_receivedEarlySizeChange{ false },
_originalWidth{ -1 },
_originalHeight{ -1 }
_originalHeight{ -1 },
_runAsComServer{ runAsComServer }
{
}
#endif
@ -128,6 +133,7 @@ private:
short _width;
short _height;
bool _runAsComServer;
bool _createServerHandle;
DWORD _serverHandle;
DWORD _signalHandle;
@ -186,6 +192,7 @@ namespace WEX
L"Use Signal Handle: '%ws'\r\n"
L"Signal Handle: '0x%x'\r\n",
L"Inherit Cursor: '%ws'\r\n",
L"Run As Com Server: '%ws'\r\n",
ci.GetClientCommandline().c_str(),
s_ToBoolString(ci.HasVtHandles()),
ci.GetVtInHandle(),
@ -199,7 +206,8 @@ namespace WEX
ci.GetServerHandle(),
s_ToBoolString(ci.HasSignalHandle()),
ci.GetSignalHandle(),
s_ToBoolString(ci.GetInheritCursor()));
s_ToBoolString(ci.GetInheritCursor()),
s_ToBoolString(ci.ShouldRunAsComServer()));
}
private:

View File

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CConsoleHandoff.h"
#include "srvinit.h"
// 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)
{
RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS));
return S_OK;
}
// Routine Description:
// - Takes the incoming information from COM and and prepares a console hosting session in this process.
// Arguments:
// - server - Console driver server handle
// - inputEvent - Event already established that we signal when new input data is available in case the driver is waiting on us
// - msg - Portable attach message containing just enough descriptor payload to get us started in servicing it
HRESULT CConsoleHandoff::EstablishHandoff(HANDLE server,
HANDLE inputEvent,
PCCONSOLE_PORTABLE_ATTACH_MSG msg)
try
{
// Fill the descriptor portion of a fresh api message with the received data.
// The descriptor portion is the "received" packet from the last ask of the driver.
// The other portions are unnecessary as they track the other buffer state, error codes,
// and the return portion of the api message.
// We will re-retrieve the connect information (title, window state, etc.) when the
// new console session begins servicing this.
CONSOLE_API_MSG apiMsg{};
apiMsg.Descriptor.Identifier.HighPart = msg->IdHighPart;
apiMsg.Descriptor.Identifier.LowPart = msg->IdLowPart;
apiMsg.Descriptor.Process = static_cast<decltype(apiMsg.Descriptor.Process)>(msg->Process);
apiMsg.Descriptor.Object = static_cast<decltype(apiMsg.Descriptor.Object)>(msg->Object);
apiMsg.Descriptor.Function = msg->Function;
apiMsg.Descriptor.InputSize = msg->InputSize;
apiMsg.Descriptor.OutputSize = msg->OutputSize;
// 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(server, server));
RETURN_IF_FAILED(_duplicateHandle(inputEvent, inputEvent));
// Now perform the handoff.
RETURN_IF_FAILED(ConsoleEstablishHandoff(server, inputEvent, &apiMsg));
return S_OK;
}
CATCH_RETURN();

View File

@ -0,0 +1,42 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CConsoleHandoff.h
Abstract:
- This module receives a console session handoff from the operating system to
an out-of-band, out-of-box console host.
Author(s):
- Michael Niksa (MiNiksa) 31-Aug-2020
--*/
#pragma once
#include "IConsoleHandoff.h"
#if defined(WT_BRANDING_RELEASE)
#define __CLSID_CConsoleHandoff "2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"
#elif defined(WT_BRANDING_PREVIEW)
#define __CLSID_CConsoleHandoff "06EC847C-C0A5-46B8-92CB-7C92F6E35CD5"
#else
#define __CLSID_CConsoleHandoff "1F9F2BF5-5BC3-4F17-B0E6-912413F1F451"
#endif
using namespace Microsoft::WRL;
struct __declspec(uuid(__CLSID_CConsoleHandoff))
CConsoleHandoff : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IConsoleHandoff>
{
#pragma region IConsoleHandoff
STDMETHODIMP EstablishHandoff(HANDLE server,
HANDLE inputEvent,
PCCONSOLE_PORTABLE_ATTACH_MSG msg);
#pragma endregion
};
CoCreatableClass(CConsoleHandoff);

View File

@ -8,9 +8,10 @@
<TargetName>OpenConsole</TargetName>
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<Import Project="..\..\common.build.pre.props" />
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClInclude Include="..\precomp.h" />
<ClInclude Include="CConsoleHandoff.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
@ -18,7 +19,8 @@
<PrecompiledHeader>Create</PrecompiledHeader>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ClCompile Include="..\exemain.cpp" />
<ClCompile Include="CConsoleHandoff.cpp" />
<ClCompile Include="exemain.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\buffer\out\lib\bufferout.vcxproj">
@ -80,7 +82,7 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..;$(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AllowIsolation>true</AllowIsolation>

View File

@ -21,12 +21,18 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CConsoleHandoff.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\precomp.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\exemain.cpp">
<ClCompile Include="CConsoleHandoff.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="exemain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

334
src/host/exe/exemain.cpp Normal file
View File

@ -0,0 +1,334 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "ConsoleArguments.hpp"
#include "srvinit.h"
#include "CConsoleHandoff.h"
#include "../server/Entrypoints.h"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../inc/conint.h"
// Define TraceLogging provider
TRACELOGGING_DEFINE_PROVIDER(
g_ConhostLauncherProvider,
"Microsoft.Windows.Console.Launcher",
// {770aa552-671a-5e97-579b-151709ec0dbd}
(0x770aa552, 0x671a, 0x5e97, 0x57, 0x9b, 0x15, 0x17, 0x09, 0xec, 0x0d, 0xbd),
TraceLoggingOptionMicrosoftTelemetry());
// Define a specialization of WRL::Module so we can specify a REGCLS_SINGLEUSE type server.
// We would like to use all the conveniences afforded to us by WRL::Module<T>, but it only
// creates REGCLS_MULTIPLEUSE with no override. This makes an override for it by taking advantage
// of its existing virtual declarations.
#pragma region Single Use Out of Proc Specialization
template<int RegClsType>
class DefaultOutOfProcModuleWithRegistrationFlag;
template<int RegClsType, typename ModuleT = DefaultOutOfProcModuleWithRegistrationFlag<RegClsType>>
class OutOfProcModuleWithRegistrationFlag : public Microsoft::WRL::Module<Microsoft::WRL::ModuleType::OutOfProc, ModuleT>
{
using Elsewhere = Module<OutOfProc, ModuleT>;
using Super = Details::OutOfProcModuleBase<ModuleT>;
public:
STDMETHOD(RegisterCOMObject)
(_In_opt_z_ const wchar_t* serverName, _In_reads_(count) IID* clsids, _In_reads_(count) IClassFactory** factories, _Inout_updates_(count) DWORD* cookies, unsigned int count)
{
return Microsoft::WRL::Details::RegisterCOMObject<RegClsType>(serverName, clsids, factories, cookies, count);
}
};
template<int RegClsType>
class DefaultOutOfProcModuleWithRegistrationFlag : public OutOfProcModuleWithRegistrationFlag<RegClsType, DefaultOutOfProcModuleWithRegistrationFlag<RegClsType>>
{
};
#pragma endregion
// Holds the wwinmain open until COM tells us there are no more server connections
wil::unique_event _comServerExitEvent;
static bool ConhostV2ForcedInRegistry()
{
// If the registry value doesn't exist, or exists and is non-zero, we should default to using the v2 console.
// Otherwise, in the case of an explicit value of 0, we should use the legacy console.
bool fShouldUseConhostV2 = true;
PCSTR pszErrorDescription = nullptr;
bool fIgnoreError = false;
// open HKCU\Console
wil::unique_hkey hConsoleSubKey;
LONG lStatus = NTSTATUS_FROM_WIN32(RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ, &hConsoleSubKey));
if (ERROR_SUCCESS == lStatus)
{
// now get the value of the ForceV2 reg value, if it exists
DWORD dwValue;
DWORD dwType;
DWORD cbValue = sizeof(dwValue);
lStatus = RegQueryValueExW(hConsoleSubKey.get(),
L"ForceV2",
nullptr,
&dwType,
(PBYTE)&dwValue,
&cbValue);
if (ERROR_SUCCESS == lStatus &&
dwType == REG_DWORD && // response is a DWORD
cbValue == sizeof(dwValue)) // response data exists
{
// Value exists. If non-zero use v2 console.
fShouldUseConhostV2 = dwValue != 0;
}
else
{
pszErrorDescription = "RegQueryValueKey Failed";
fIgnoreError = lStatus == ERROR_FILE_NOT_FOUND;
}
}
else
{
pszErrorDescription = "RegOpenKey Failed";
// ignore error caused by RegOpenKey if it's a simple case of the key not being found
fIgnoreError = lStatus == ERROR_FILE_NOT_FOUND;
}
return fShouldUseConhostV2;
}
[[nodiscard]] static HRESULT ValidateServerHandle(const HANDLE handle)
{
// Make sure this is a console file.
FILE_FS_DEVICE_INFORMATION DeviceInformation;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS const Status = NtQueryVolumeInformationFile(handle, &IoStatusBlock, &DeviceInformation, sizeof(DeviceInformation), FileFsDeviceInformation);
if (!NT_SUCCESS(Status))
{
RETURN_NTSTATUS(Status);
}
else if (DeviceInformation.DeviceType != FILE_DEVICE_CONSOLE)
{
return E_INVALIDARG;
}
else
{
return S_OK;
}
}
static bool ShouldUseLegacyConhost(const ConsoleArguments& args)
{
if (args.InConptyMode())
{
return false;
}
if (args.GetForceV1())
{
return true;
}
// Per the documentation in ConhostV2ForcedInRegistry, it checks the value
// of HKCU\Console:ForceV2. If it's *not found* or nonzero, "v2" is forced.
return !ConhostV2ForcedInRegistry();
}
[[nodiscard]] static HRESULT ActivateLegacyConhost(const HANDLE handle)
{
HRESULT hr = S_OK;
// TraceLog that we're using the legacy console. We won't log new console
// because there's already a count of how many total processes were launched.
// Total - legacy = new console.
// We expect legacy launches to be infrequent enough to not cause an issue.
TraceLoggingWrite(g_ConhostLauncherProvider, "IsLegacyLoaded", TraceLoggingBool(true, "ConsoleLegacy"), TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY));
const PCWSTR pszConhostDllName = L"ConhostV1.dll";
// Load our implementation, and then Load/Launch the IO thread.
wil::unique_hmodule hConhostBin(LoadLibraryExW(pszConhostDllName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
if (hConhostBin.get() != nullptr)
{
typedef NTSTATUS (*PFNCONSOLECREATEIOTHREAD)(__in HANDLE Server);
PFNCONSOLECREATEIOTHREAD pfnConsoleCreateIoThread = (PFNCONSOLECREATEIOTHREAD)GetProcAddress(hConhostBin.get(), "ConsoleCreateIoThread");
if (pfnConsoleCreateIoThread != nullptr)
{
hr = HRESULT_FROM_NT(pfnConsoleCreateIoThread(handle));
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
else
{
// setup status error
hr = HRESULT_FROM_WIN32(GetLastError());
}
if (SUCCEEDED(hr))
{
hConhostBin.release();
}
return hr;
}
// Routine Description:
// - Called back when COM says there is nothing left for our server to do and we can tear down.
static void _releaseNotifier() noexcept
{
_comServerExitEvent.SetEvent();
}
// Routine Description:
// - Main entry point for EXE version of console launching.
// This can be used as a debugging/diagnostics tool as well as a method of testing the console without
// replacing the system binary.
// Arguments:
// - hInstance - This module instance pointer is saved for resource lookups.
// - hPrevInstance - Unused pointer to the module instances. See wWinMain definitions @ MSDN for more details.
// - pwszCmdLine - Unused variable. We will look up the command line using GetCommandLineW().
// - nCmdShow - Unused variable specifying window show/hide state for Win32 mode applications.
// Return value:
// - [[noreturn]] - This function will not return. It will kill the thread we were called from and the console server threads will take over.
int CALLBACK wWinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE /*hPrevInstance*/,
_In_ PWSTR /*pwszCmdLine*/,
_In_ int /*nCmdShow*/)
{
Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().hInstance = hInstance;
ConsoleCheckDebug();
// Set up OutOfProc COM server stuff in case we become one.
// WRL Module gets going right before winmain is called, so if we don't
// set this up appropriately... other things using WRL that aren't us
// could get messed up by the singleton module and cause unexpected errors.
_comServerExitEvent.create();
// We will use a single use server to ensure that each out-of-box console that
// gets activated to take over a session from the OS console will only be responsible
// for ONE console server session. This ensures that we, as the handoff target, are
// responsible for only one session and one server handle to the driver and we maintain
// the one-to-one relationship between console sessions and servers just like the inbox
// one. Theoretically we could combine them if we had any way of keeping track of all
// of the console state separately per server connection... but that's not how this is
// all designed (so many globals) and it would potentially risk one session's crash
// taking down some completely unrelated command-line clients.
// ----
// The general flow is...
// 1. The in-box console looks up the registered delegation console
// 2. An OpenConsole.exe is typically found which is a newer version of the
// same code that is in-box and may have more bug fixes or features (especially
// an improved VT dialect or something of that ilk).
// 3. By activating the registered CLSID, the in-box console will be starting `openconsole.exe -Embedding`
// through the OutOfProc COM server infrastructure.
// 4. The `openconsole.exe -Embedding` that starts will come through right here and register
// `CConsoleHandoff` to accept ONE connection.
// 5. The in-box console will then receive an `IConsoleHandoff` to this `CConsoleHandoff` and the registration
// immediately expires, letting no one else in. The next caller will start another new `openconsole.exe -Embedding` process.
// 6. The in-box console invokes the handoff method on the interface and it transfers some data into `CConsoleHandoff`
// of the `OpenConsole.exe` which will then stand up its own server IO thread and handle all console server session
// messages going forward.
// 7. The out-of-box `OpenConsole.exe` can then attempt to lookup and invoke a `CTerminalHandoff` to ask a registered
// Terminal to become the UI. This OpenConsole.exe will put itself in PTY mode and let the Terminal handle user interaction.
auto& module = OutOfProcModuleWithRegistrationFlag<REGCLS_SINGLEUSE>::Create(&_releaseNotifier);
// Register Trace provider by GUID
TraceLoggingRegister(g_ConhostLauncherProvider);
// Pass command line and standard handles at this point in time as
// potential preferences for execution that were passed on process creation.
ConsoleArguments args(GetCommandLineW(),
GetStdHandle(STD_INPUT_HANDLE),
GetStdHandle(STD_OUTPUT_HANDLE));
HRESULT hr = args.ParseCommandline();
if (SUCCEEDED(hr))
{
// Only try to register as a handoff target if we are NOT a part of Windows.
#ifndef __INSIDE_WINDOWS
bool defAppEnabled = false;
if (args.ShouldRunAsComServer() && SUCCEEDED(Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy(defAppEnabled)) && defAppEnabled)
{
try
{
// OK we have to do this here and not in another method because
// we would either have to store the module ref above in some accessible
// variable (which would be awful because of the gigantic template name)
// or we would have to come up with some creativity to extract it out
// of the singleton module base without accidentally having WRL
// think we're recreating it (and then assert because it's already created.)
//
// Also this is all a problem because the decrementing count of used objects
// in this module in WRL::Module base doesn't null check the release notifier
// callback function in the OutOfProc variant in the 18362 SDK. So if anything
// else uses WRL directly or indirectly, it'll crash if the refcount
// ever hits 0.
// It does in the 19041 SDK so this can be cleaned into its own class if
// we ever build with 19041 or later.
auto comScope{ wil::CoInitializeEx(COINIT_MULTITHREADED) };
RETURN_IF_FAILED(module.RegisterObjects());
_comServerExitEvent.wait();
RETURN_IF_FAILED(module.UnregisterObjects());
}
CATCH_RETURN()
}
else
#endif
{
if (ShouldUseLegacyConhost(args))
{
if (args.ShouldCreateServerHandle())
{
hr = E_INVALIDARG;
}
else
{
hr = ValidateServerHandle(args.GetServerHandle());
if (SUCCEEDED(hr))
{
hr = ActivateLegacyConhost(args.GetServerHandle());
}
}
}
else
{
if (args.ShouldCreateServerHandle())
{
hr = Entrypoints::StartConsoleForCmdLine(args.GetClientCommandline().c_str(), &args);
}
else
{
hr = ValidateServerHandle(args.GetServerHandle());
if (SUCCEEDED(hr))
{
hr = Entrypoints::StartConsoleForServerHandle(args.GetServerHandle(), &args);
}
}
}
}
}
// Unregister Tracelogging
TraceLoggingUnregister(g_ConhostLauncherProvider);
// Only do this if startup was successful. Otherwise, this will leave conhost.exe running with no hosted application.
if (SUCCEEDED(hr))
{
// Since the lifetime of conhost.exe is inextricably tied to the lifetime of its client processes we set our process
// shutdown priority to zero in order to effectively opt out of shutdown process enumeration. Conhost will exit when
// all of its client processes do.
SetProcessShutdownParameters(0, 0);
ExitThread(hr);
}
return hr;
}

View File

@ -32,7 +32,8 @@ CETCOMPAT=1
SOURCES = \
$(SOURCES) \
..\exemain.cpp \
.\CConsoleHandoff.cpp \
.\exemain.cpp \
..\res.rc \
# -------------------------------------

View File

@ -1,227 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "ConsoleArguments.hpp"
#include "srvinit.h"
#include "../server/Entrypoints.h"
#include "../interactivity/inc/ServiceLocator.hpp"
// Define TraceLogging provider
TRACELOGGING_DEFINE_PROVIDER(
g_ConhostLauncherProvider,
"Microsoft.Windows.Console.Launcher",
// {770aa552-671a-5e97-579b-151709ec0dbd}
(0x770aa552, 0x671a, 0x5e97, 0x57, 0x9b, 0x15, 0x17, 0x09, 0xec, 0x0d, 0xbd),
TraceLoggingOptionMicrosoftTelemetry());
static bool ConhostV2ForcedInRegistry()
{
// If the registry value doesn't exist, or exists and is non-zero, we should default to using the v2 console.
// Otherwise, in the case of an explicit value of 0, we should use the legacy console.
bool fShouldUseConhostV2 = true;
PCSTR pszErrorDescription = nullptr;
bool fIgnoreError = false;
// open HKCU\Console
wil::unique_hkey hConsoleSubKey;
LONG lStatus = NTSTATUS_FROM_WIN32(RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ, &hConsoleSubKey));
if (ERROR_SUCCESS == lStatus)
{
// now get the value of the ForceV2 reg value, if it exists
DWORD dwValue;
DWORD dwType;
DWORD cbValue = sizeof(dwValue);
lStatus = RegQueryValueExW(hConsoleSubKey.get(),
L"ForceV2",
nullptr,
&dwType,
(PBYTE)&dwValue,
&cbValue);
if (ERROR_SUCCESS == lStatus &&
dwType == REG_DWORD && // response is a DWORD
cbValue == sizeof(dwValue)) // response data exists
{
// Value exists. If non-zero use v2 console.
fShouldUseConhostV2 = dwValue != 0;
}
else
{
pszErrorDescription = "RegQueryValueKey Failed";
fIgnoreError = lStatus == ERROR_FILE_NOT_FOUND;
}
}
else
{
pszErrorDescription = "RegOpenKey Failed";
// ignore error caused by RegOpenKey if it's a simple case of the key not being found
fIgnoreError = lStatus == ERROR_FILE_NOT_FOUND;
}
return fShouldUseConhostV2;
}
[[nodiscard]] static HRESULT ValidateServerHandle(const HANDLE handle)
{
// Make sure this is a console file.
FILE_FS_DEVICE_INFORMATION DeviceInformation;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS const Status = NtQueryVolumeInformationFile(handle, &IoStatusBlock, &DeviceInformation, sizeof(DeviceInformation), FileFsDeviceInformation);
if (!NT_SUCCESS(Status))
{
RETURN_NTSTATUS(Status);
}
else if (DeviceInformation.DeviceType != FILE_DEVICE_CONSOLE)
{
return E_INVALIDARG;
}
else
{
return S_OK;
}
}
static bool ShouldUseLegacyConhost(const ConsoleArguments& args)
{
if (args.InConptyMode())
{
return false;
}
if (args.GetForceV1())
{
return true;
}
// Per the documentation in ConhostV2ForcedInRegistry, it checks the value
// of HKCU\Console:ForceV2. If it's *not found* or nonzero, "v2" is forced.
return !ConhostV2ForcedInRegistry();
}
[[nodiscard]] static HRESULT ActivateLegacyConhost(const HANDLE handle)
{
HRESULT hr = S_OK;
// TraceLog that we're using the legacy console. We won't log new console
// because there's already a count of how many total processes were launched.
// Total - legacy = new console.
// We expect legacy launches to be infrequent enough to not cause an issue.
TraceLoggingWrite(g_ConhostLauncherProvider, "IsLegacyLoaded", TraceLoggingBool(true, "ConsoleLegacy"), TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY));
const PCWSTR pszConhostDllName = L"ConhostV1.dll";
// Load our implementation, and then Load/Launch the IO thread.
wil::unique_hmodule hConhostBin(LoadLibraryExW(pszConhostDllName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
if (hConhostBin.get() != nullptr)
{
typedef NTSTATUS (*PFNCONSOLECREATEIOTHREAD)(__in HANDLE Server);
PFNCONSOLECREATEIOTHREAD pfnConsoleCreateIoThread = (PFNCONSOLECREATEIOTHREAD)GetProcAddress(hConhostBin.get(), "ConsoleCreateIoThread");
if (pfnConsoleCreateIoThread != nullptr)
{
hr = HRESULT_FROM_NT(pfnConsoleCreateIoThread(handle));
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
else
{
// setup status error
hr = HRESULT_FROM_WIN32(GetLastError());
}
if (SUCCEEDED(hr))
{
hConhostBin.release();
}
return hr;
}
// Routine Description:
// - Main entry point for EXE version of console launching.
// This can be used as a debugging/diagnostics tool as well as a method of testing the console without
// replacing the system binary.
// Arguments:
// - hInstance - This module instance pointer is saved for resource lookups.
// - hPrevInstance - Unused pointer to the module instances. See wWinMain definitions @ MSDN for more details.
// - pwszCmdLine - Unused variable. We will look up the command line using GetCommandLineW().
// - nCmdShow - Unused variable specifying window show/hide state for Win32 mode applications.
// Return value:
// - [[noreturn]] - This function will not return. It will kill the thread we were called from and the console server threads will take over.
int CALLBACK wWinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE /*hPrevInstance*/,
_In_ PWSTR /*pwszCmdLine*/,
_In_ int /*nCmdShow*/)
{
Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().hInstance = hInstance;
ConsoleCheckDebug();
// Register Trace provider by GUID
TraceLoggingRegister(g_ConhostLauncherProvider);
// Pass command line and standard handles at this point in time as
// potential preferences for execution that were passed on process creation.
ConsoleArguments args(GetCommandLineW(),
GetStdHandle(STD_INPUT_HANDLE),
GetStdHandle(STD_OUTPUT_HANDLE));
HRESULT hr = args.ParseCommandline();
if (SUCCEEDED(hr))
{
if (ShouldUseLegacyConhost(args))
{
if (args.ShouldCreateServerHandle())
{
hr = E_INVALIDARG;
}
else
{
hr = ValidateServerHandle(args.GetServerHandle());
if (SUCCEEDED(hr))
{
hr = ActivateLegacyConhost(args.GetServerHandle());
}
}
}
else
{
if (args.ShouldCreateServerHandle())
{
hr = Entrypoints::StartConsoleForCmdLine(args.GetClientCommandline().c_str(), &args);
}
else
{
hr = ValidateServerHandle(args.GetServerHandle());
if (SUCCEEDED(hr))
{
hr = Entrypoints::StartConsoleForServerHandle(args.GetServerHandle(), &args);
}
}
}
}
// Unregister Tracelogging
TraceLoggingUnregister(g_ConhostLauncherProvider);
// Only do this if startup was successful. Otherwise, this will leave conhost.exe running with no hosted application.
if (SUCCEEDED(hr))
{
// Since the lifetime of conhost.exe is inextricably tied to the lifetime of its client processes we set our process
// shutdown priority to zero in order to effectively opt out of shutdown process enumeration. Conhost will exit when
// all of its client processes do.
SetProcessShutdownParameters(0, 0);
ExitThread(hr);
}
return hr;
}

View File

@ -73,6 +73,7 @@ public:
bool handoffTarget = false;
std::optional<CLSID> handoffConsoleClsid;
std::optional<CLSID> handoffTerminalClsid;
#ifdef UNIT_TESTING
void EnableConptyModeForTests(std::unique_ptr<Microsoft::Console::Render::VtEngine> vtRenderEngine);

View File

@ -121,7 +121,7 @@
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>

View File

@ -6,7 +6,7 @@
<RootNamespace>hostlib</RootNamespace>
<ProjectName>Host</ProjectName>
<TargetName>ConhostV2Lib</TargetName>
<ConfigurationType>StaticLibrary</ConfigurationType>
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<!-- DONT ADD NEW FILES HERE, ADD THEM TO host-common.vcxitems -->

View File

@ -30,9 +30,6 @@
<ClCompile Include="..\convarea.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\cursor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\dbcs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -162,9 +159,6 @@
<ClCompile Include="..\PtySignalInputThread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\CodepointWidthDetector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\conareainfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -183,6 +177,12 @@
<ClCompile Include="..\CopyToCharPopup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\CursorBlinker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ScreenBufferRenderTarget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h">
@ -254,9 +254,6 @@
<ClInclude Include="..\conwinuserrefs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\cursor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\dbcs.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -332,9 +329,6 @@
<ClInclude Include="..\history.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\CodepointWidthDetector.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\conareainfo.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -353,5 +347,17 @@
<ClInclude Include="..\CopyToCharPopup.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\CursorBlinker.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\IIoProvider.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ScreenBufferRenderTarget.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
</Project>

View File

@ -1,17 +1,14 @@
<?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>
<ProjectGuid>{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>openconsoleproxy</RootNamespace>
<ProjectName>OpenConsoleProxy</ProjectName>
<TargetName>OpenConsoleProxy</TargetName>
<ConfigurationType>Utility</ConfigurationType>
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClInclude Include="$(IntDir)\IConsoleHandoff.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="IConsoleHandoff.idl">
<!--
@ -25,12 +22,48 @@
<MinimumTargetSystem>NT100</MinimumTargetSystem>
<OutputDirectory>$(IntDir)</OutputDirectory>
</Midl>
<Midl Include="ITerminalHandoff.idl">
<HeaderFileName>ITerminalHandoff.h</HeaderFileName>
<MinimumTargetSystem>NT100</MinimumTargetSystem>
<OutputDirectory>$(IntDir)</OutputDirectory>
</Midl>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(IntDir)\IConsoleHandoff.h" />
<ClInclude Include="$(IntDir)\ITerminalHandoff.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(IntDir)\dlldata.c" />
<ClCompile Include="$(IntDir)\IConsoleHandoff_i.c" />
<ClCompile Include="$(IntDir)\IConsoleHandoff_p.c" />
<ClCompile Include="$(IntDir)\ITerminalHandoff_i.c" />
<ClCompile Include="$(IntDir)\ITerminalHandoff_p.c" />
</ItemGroup>
<ItemDefinitionGroup Condition="'$(WindowsTerminalBranding)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>PROXY_CLSID_IS={0x3171DE52,0x6EFA,0x4AEF,{0x8A,0x9F,0xD0,0x2B,0xD6,0x7E,0x7A,0x4F}};%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(WindowsTerminalBranding)'=='Preview'">
<ClCompile>
<PreprocessorDefinitions>PROXY_CLSID_IS={0x1833E661,0xCC81,0x4DD0,{0x87,0xC6,0xC2,0xF7,0x4B,0xD3,0x9E,0xFA}};%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(WindowsTerminalBranding)'==''">
<ClCompile>
<PreprocessorDefinitions>PROXY_CLSID_IS={0xDEC4804D,0x56D1,0x4F73,{0x9F,0xBE,0x68,0x28,0xE7,0xC8,0x5C,0x56}};%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<CallingConvention>StdCall</CallingConvention> <!-- Must be Stdcall on all platforms to resolve _ObjectStublessClient3 -->
<PreprocessorDefinitions>REGISTER_PROXY_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<Link>
<ModuleDefinitionFile>OpenConsoleProxy.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />

View File

@ -14,11 +14,31 @@
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(IntDir)\dlldata.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)\IConsoleHandoff_i.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)\IConsoleHandoff_p.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)\ITerminalHandoff_i.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)\ITerminalHandoff_p.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="IConsoleHandoff_h.h">
<ClInclude Include="$(IntDir)\IConsoleHandoff.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(IntDir)\ITerminalHandoff.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -26,5 +46,8 @@
<Midl Include="IConsoleHandoff.idl">
<Filter>Source Files</Filter>
</Midl>
<Midl Include="ITerminalHandoff.idl">
<Filter>Source Files</Filter>
</Midl>
</ItemGroup>
</Project>

View File

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "oaidl.idl";
import "ocidl.idl";
@ -24,3 +27,4 @@ typedef const CONSOLE_PORTABLE_ATTACH_MSG* PCCONSOLE_PORTABLE_ATTACH_MSG;
[in, system_handle(sh_event)] HANDLE inputEvent,
[in, ref] PCCONSOLE_PORTABLE_ATTACH_MSG msg);
};

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74)
] 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_process)] HANDLE client);
};

View File

@ -0,0 +1,8 @@
LIBRARY OpenConsoleProxy
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
GetProxyDllInfo

View File

@ -23,6 +23,7 @@
#include "renderData.hpp"
#include "../renderer/base/renderer.hpp"
#include "ITerminalHandoff.h"
#include "../inc/conint.h"
#include "../propslib/DelegationConfig.hpp"
@ -60,6 +61,10 @@ try
{
Globals.handoffConsoleClsid = delegationClsid;
}
if (SUCCEEDED(DelegationConfig::s_GetDefaultTerminalId(delegationClsid)))
{
Globals.handoffTerminalClsid = delegationClsid;
}
}
// Removed allocation of scroll buffer here.
@ -265,20 +270,53 @@ void ConsoleCheckDebug()
#endif
}
[[nodiscard]] HRESULT ConsoleCreateIoThreadLegacy(_In_ HANDLE Server, const ConsoleArguments* const args)
// Routine Description:
// - Sets up the main driver message packet (I/O) processing
// thread that will handle all client requests from all
// attached command-line applications for the duration
// of this console server session.
// - The optional arguments are only used when receiving a handoff
// from another console server (typically in-box to the Windows OS image)
// that has already started processing the console session.
// They will be blank and generated internally by this method if this is the first
// console server starting in response to a client startup or ConPTY setup
// request.
// Arguments:
// - Server - Handle to the console driver that represents
// our server side of the connection.
// - args - Command-line arguments from starting this console host
// that may affect the way we host the session.
// - driverInputEvent - (Optional) Event registered with the console driver
// that we will use to wake up input read requests that
// are blocked because they came in when we had no input ready.
// - connectMessage - (Optional) A message received from a connecting client
// by another console server that is being passed off to us as a part of
// the handoff strategy.
HRESULT ConsoleCreateIoThread(_In_ HANDLE Server,
const ConsoleArguments* const args,
HANDLE driverInputEvent,
PCONSOLE_API_MSG connectMessage)
{
auto& g = ServiceLocator::LocateGlobals();
RETURN_IF_FAILED(ConsoleServerInitialization(Server, args));
RETURN_IF_FAILED(g.hConsoleInputInitEvent.create(wil::EventOptions::None));
// Set up and tell the driver about the input available event.
RETURN_IF_FAILED(g.hInputEvent.create(wil::EventOptions::ManualReset));
if (driverInputEvent != INVALID_HANDLE_VALUE)
{
// Store the driver input event. It's already been told that it exists by whomever started us.
g.hInputEvent.reset(driverInputEvent);
}
else
{
// Set up and tell the driver about the input available event.
RETURN_IF_FAILED(g.hInputEvent.create(wil::EventOptions::ManualReset));
CD_IO_SERVER_INFORMATION ServerInformation;
ServerInformation.InputAvailableEvent = ServiceLocator::LocateGlobals().hInputEvent.get();
RETURN_IF_FAILED(g.pDeviceComm->SetServerInformation(&ServerInformation));
CD_IO_SERVER_INFORMATION ServerInformation;
ServerInformation.InputAvailableEvent = ServiceLocator::LocateGlobals().hInputEvent.get();
RETURN_IF_FAILED(g.pDeviceComm->SetServerInformation(&ServerInformation));
}
HANDLE const hThread = CreateThread(nullptr, 0, ConsoleIoThread, nullptr, 0, nullptr);
HANDLE const hThread = CreateThread(nullptr, 0, ConsoleIoThread, connectMessage, 0, nullptr);
RETURN_HR_IF(E_HANDLE, hThread == nullptr);
LOG_IF_WIN32_BOOL_FALSE(CloseHandle(hThread)); // The thread will run on its own and close itself. Free the associated handle.
@ -295,6 +333,117 @@ void ConsoleCheckDebug()
return S_OK;
}
// Routine Description:
// - Accepts a console server session from another console server
// most commonly from the operating system in-box console to
// a more-up-to-date and out-of-band delivered one.
// Arguments:
// - Server - Handle to the console driver that represents our server
// side of hosting the console session
// - driverInputEvent - Handle to an event already registered with the
// driver that clients will implicitly wait on when we don't have
// any input to return in the queue when a request is made and is
// signaled to unblock them when input finally arrives.
// - connectMessage - A console driver/server message as received
// by the previous console server for us to finish processing in
// order to complete the client's initial connection and store
// all necessary callback information for all subsequent API calls.
// Return Value:
// - COM errors, registry errors, pipe errors, handle manipulation errors,
// errors from the creating the thread for the
// standard IO thread loop for the server to process messages
// from the driver... or an S_OK success.
[[nodiscard]] HRESULT ConsoleEstablishHandoff(_In_ HANDLE Server,
HANDLE driverInputEvent,
PCONSOLE_API_MSG connectMessage)
try
{
auto& g = ServiceLocator::LocateGlobals();
g.handoffTarget = true;
IID delegationClsid;
if (SUCCEEDED(DelegationConfig::s_GetDefaultConsoleId(delegationClsid)))
{
g.handoffConsoleClsid = delegationClsid;
}
if (SUCCEEDED(DelegationConfig::s_GetDefaultTerminalId(delegationClsid)))
{
g.handoffTerminalClsid = delegationClsid;
}
if (!g.handoffTerminalClsid)
{
return E_NOT_SET;
}
wil::unique_handle signalPipeTheirSide;
wil::unique_handle signalPipeOurSide;
wil::unique_handle inPipeTheirSide;
wil::unique_handle inPipeOurSide;
wil::unique_handle outPipeTheirSide;
wil::unique_handle outPipeOurSide;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
// Mark inheritable for signal handle when creating. It'll have the same value on the other side.
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = nullptr;
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(signalPipeOurSide.addressof(), signalPipeTheirSide.addressof(), nullptr, 0));
RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeTheirSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(inPipeOurSide.addressof(), inPipeTheirSide.addressof(), nullptr, 0));
RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(inPipeTheirSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(outPipeTheirSide.addressof(), outPipeOurSide.addressof(), nullptr, 0));
RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(outPipeTheirSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, TRUE, static_cast<DWORD>(connectMessage->Descriptor.Process)) };
RETURN_LAST_ERROR_IF_NULL(clientProcess.get());
::Microsoft::WRL::ComPtr<ITerminalHandoff> handoff;
RETURN_IF_FAILED(CoCreateInstance(g.handoffTerminalClsid.value(), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&handoff)));
RETURN_IF_FAILED(handoff->EstablishPtyHandoff(inPipeTheirSide.get(),
outPipeTheirSide.get(),
signalPipeTheirSide.get(),
clientProcess.get()));
inPipeTheirSide.release();
outPipeTheirSide.release();
signalPipeTheirSide.release();
const auto commandLine = fmt::format(FMT_COMPILE(L" --headless --signal {:#x}"), (int64_t)signalPipeOurSide.release());
ConsoleArguments consoleArgs(commandLine, inPipeOurSide.release(), outPipeOurSide.release());
RETURN_IF_FAILED(consoleArgs.ParseCommandline());
return ConsoleCreateIoThread(Server, &consoleArgs, driverInputEvent, connectMessage);
}
CATCH_RETURN()
// Routine Description:
// - Creates the I/O thread for handling and processing messages from the console driver
// as the server side of a console session.
// - This entrypoint is for all start scenarios that are not receiving a hand-off
// from another console server. For example, getting started by kernelbase.dll from
// the operating system as a client application realizes it needs a console server,
// getting started to be a ConPTY host inside the OS, or being double clicked either
// inside the OS as `conhost.exe` or outside as `OpenConsole.exe`.
// Arguments:
// - Server - The server side handle to the console driver to let us pick up messages to process for the clients.
// - args - A structure of arguments that may have been passed in on the command-line, typically only used to control the ConPTY configuration.
// Return Value:
// - S_OK if the thread starts up correctly or any number of thread, registry, windowing, or just about any other
// failure that could possibly occur during console server initialization.
[[nodiscard]] HRESULT ConsoleCreateIoThreadLegacy(_In_ HANDLE Server, const ConsoleArguments* const args)
{
return ConsoleCreateIoThread(Server, args, INVALID_HANDLE_VALUE, nullptr);
}
#define SYSTEM_ROOT (L"%SystemRoot%")
#define SYSTEM_ROOT_LENGTH (sizeof(SYSTEM_ROOT) - sizeof(WCHAR))
@ -662,10 +811,10 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand,
// - This routine is the main one in the console server IO thread.
// - It reads IO requests submitted by clients through the driver, services and completes them in a loop.
// Arguments:
// - <none>
// - lpParameter - PCONSOLE_API_MSG being handed off to us from the previous I/O.
// Return Value:
// - This routine never returns. The process exits when no more references or clients exist.
DWORD WINAPI ConsoleIoThread(LPVOID /*lpParameter*/)
DWORD WINAPI ConsoleIoThread(LPVOID lpParameter)
{
auto& globals = ServiceLocator::LocateGlobals();
@ -674,6 +823,15 @@ DWORD WINAPI ConsoleIoThread(LPVOID /*lpParameter*/)
ReceiveMsg._pDeviceComm = globals.pDeviceComm;
PCONSOLE_API_MSG ReplyMsg = nullptr;
// If we were given a message on startup, process that in our context and then continue with the IO loop normally.
if (lpParameter)
{
ReceiveMsg = *(PCONSOLE_API_MSG)lpParameter;
ReceiveMsg._pApiRoutines = &globals.api;
ReceiveMsg._pDeviceComm = globals.pDeviceComm;
IoSorter::ServiceIoOperation(&ReceiveMsg, &ReplyMsg);
}
bool fShouldExit = false;
while (!fShouldExit)
{

View File

@ -28,4 +28,8 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand,
[[nodiscard]] bool ConsoleConnectionDeservesVisibleWindow(PCONSOLE_API_CONNECTINFO p);
[[nodiscard]] HRESULT ConsoleEstablishHandoff(_In_ HANDLE Server,
HANDLE driverInputEvent,
PCONSOLE_API_MSG connectMessage);
void ConsoleCheckDebug();

View File

@ -81,7 +81,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe \"this is the commandline\"";
@ -101,7 +102,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless \"--vtmode bar this is the commandline\"";
@ -121,7 +123,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless --server 0x4 this is the commandline";
@ -141,7 +144,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
false, // createServerHandle
0x4, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless\t--vtmode\txterm\tthis\tis\tthe\tcommandline";
@ -161,7 +165,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless\\ foo\\ --outpipe\\ bar\\ this\\ is\\ the\\ commandline";
@ -181,7 +186,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless\\\tfoo\\\t--outpipe\\\tbar\\\tthis\\\tis\\\tthe\\\tcommandline";
@ -201,7 +207,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --vtmode a\\\\\\\\\"b c\" d e";
@ -221,7 +228,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe this is the commandline";
@ -241,7 +249,8 @@ void ConsoleArgumentsTests::ArgSplittingTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
}
@ -266,7 +275,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe foo";
@ -286,7 +296,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe foo -- bar";
@ -306,7 +317,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --vtmode foo foo -- bar";
@ -326,7 +338,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe console --vtmode foo foo -- bar";
@ -346,7 +359,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe console --vtmode foo --outpipe foo -- bar";
@ -366,7 +380,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --vtmode foo -- --outpipe foo bar";
@ -386,7 +401,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --vtmode -- --headless bar";
@ -406,7 +422,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --";
@ -426,7 +443,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe";
@ -446,7 +464,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
}
@ -471,7 +490,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --server 0x4";
@ -491,7 +511,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe 0x4 0x8";
@ -511,7 +532,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --server 0x4 0x8";
@ -531,7 +553,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe 0x4 --server 0x8";
@ -551,7 +574,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --server 0x4 --server 0x8";
@ -571,7 +595,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe 0x4 -ForceV1";
@ -591,7 +616,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe -ForceV1";
@ -611,7 +637,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
}
@ -660,7 +687,8 @@ void ConsoleArgumentsTests::CombineVtPipeHandleTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --vtmode xterm-256color";
@ -680,7 +708,8 @@ void ConsoleArgumentsTests::CombineVtPipeHandleTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
}
@ -715,7 +744,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --width 120";
@ -735,7 +765,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --height 30";
@ -755,7 +786,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --width 0";
@ -775,7 +807,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --width -1";
@ -795,7 +828,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --width foo";
@ -815,7 +849,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --width 2foo";
@ -835,7 +870,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --width 65535";
@ -855,7 +891,8 @@ void ConsoleArgumentsTests::InitialSizeTests()
true, // createServerHandle
0ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
}
@ -880,7 +917,8 @@ void ConsoleArgumentsTests::HeadlessArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless 0x4";
@ -900,7 +938,8 @@ void ConsoleArgumentsTests::HeadlessArgTests()
false, // createServerHandle
4ul, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --headless --headless";
@ -920,7 +959,8 @@ void ConsoleArgumentsTests::HeadlessArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe -- foo.exe --headless";
@ -940,7 +980,8 @@ void ConsoleArgumentsTests::HeadlessArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
}
@ -969,7 +1010,8 @@ void ConsoleArgumentsTests::SignalHandleTests()
false, // createServerHandle
4ul, // serverHandle
8ul, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --server 0x4 --signal ASDF";
@ -989,7 +1031,8 @@ void ConsoleArgumentsTests::SignalHandleTests()
false, // createServerHandle
4ul, // serverHandle
0ul, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --signal --server 0x4";
@ -1009,7 +1052,8 @@ void ConsoleArgumentsTests::SignalHandleTests()
true, // createServerHandle
0ul, // serverHandle
0ul, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
}
@ -1038,7 +1082,8 @@ void ConsoleArgumentsTests::FeatureArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --feature tty";
ArgTestsRunner(L"#2 Error case, pass an unsupported feature",
@ -1057,7 +1102,8 @@ void ConsoleArgumentsTests::FeatureArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --feature pty --feature pty";
@ -1077,7 +1123,8 @@ void ConsoleArgumentsTests::FeatureArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
true); // successful parse?
commandline = L"conhost.exe --feature pty --feature tty";
@ -1097,7 +1144,8 @@ void ConsoleArgumentsTests::FeatureArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --feature pty --feature";
@ -1117,7 +1165,8 @@ void ConsoleArgumentsTests::FeatureArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
commandline = L"conhost.exe --feature pty --feature --signal foo";
@ -1137,6 +1186,7 @@ void ConsoleArgumentsTests::FeatureArgTests()
true, // createServerHandle
0, // serverHandle
0, // signalHandle
false), // inheritCursor
false, // inheritCursor
false), // runAsComServer
false); // successful parse?
}

View File

@ -32,6 +32,8 @@ void EdpPolicy::AuditClipboard(const std::wstring_view /*destinationName*/) noex
[[nodiscard]] HRESULT DefaultApp::CheckDefaultAppPolicy(bool& isEnabled) noexcept
{
isEnabled = false;
// True so propsheet will show configuration options but be sure that
// the open one won't attempt handoff from double click of OpenConsole.exe
isEnabled = true;
return S_OK;
}
}

View File

@ -146,6 +146,19 @@ static bool _shouldAttemptHandoff(const Globals& globals,
const CONSOLE_INFORMATION& gci,
CONSOLE_API_CONNECTINFO& cac)
{
#ifndef __INSIDE_WINDOWS
UNREFERENCED_PARAMETER(globals);
UNREFERENCED_PARAMETER(gci);
UNREFERENCED_PARAMETER(cac);
// If we are outside of Windows, do not attempt a handoff
// to another target as handoff is an inbox escape mechanism
// to get to this copy!
return false;
#else
// This console is already initialized. Do not
// attempt handoff to another one.
// Note you can have a non-attach secondary connect for a child process
@ -209,6 +222,7 @@ static bool _shouldAttemptHandoff(const Globals& globals,
}
return true;
#endif
}
// Routine Description: