Compare commits

...

4 Commits

28 changed files with 706 additions and 74 deletions

View File

@@ -911,12 +911,20 @@ namespace winrt::TerminalApp::implementation
// currently displayed, it will be shown. // currently displayed, it will be shown.
// Arguments: // Arguments:
// - settings: the TerminalSettings object to use to create the TerminalControl with. // - settings: the TerminalSettings object to use to create the TerminalControl with.
void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings) void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings, std::optional<uint64_t> serverHandle)
{ {
// Initialize the new tab // Initialize the new tab
TerminalConnection::ITerminalConnection connection = nullptr;
// Create a Conhost connection based on the values in our settings object. // Create a Conhost connection based on the values in our settings object.
TerminalConnection::ITerminalConnection connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), 30, 80, winrt::guid()); if (!serverHandle)
{
connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), 30, 80, winrt::guid());
}
else
{
connection = TerminalConnection::ConhostConnection(serverHandle.value(), 30, 80, winrt::guid());
}
TermControl term{ settings, connection }; TermControl term{ settings, connection };
@@ -1158,6 +1166,33 @@ namespace winrt::TerminalApp::implementation
return { L"Windows Terminal" }; return { L"Windows Terminal" };
} }
void App::IncomingConnection(uint64_t serverHandle)
{
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, serverHandle]() {
// Getting Guid for default profile
const auto globalSettings = _settings->GlobalSettings();
auto profileGuid = globalSettings.GetDefaultProfile();
TerminalSettings settings = _settings->MakeSettings(profileGuid);
_CreateNewTabFromSettings(profileGuid, settings, serverHandle);
});
}
void App::IncomingConnection(hstring cmdline, hstring workingDir)
{
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, cmdline, workingDir]() {
// Getting Guid for default profile
const auto globalSettings = _settings->GlobalSettings();
auto profileGuid = globalSettings.GetDefaultProfile();
TerminalSettings settings = _settings->MakeSettings(profileGuid);
settings.Commandline(cmdline);
settings.StartingDirectory(workingDir);
_CreateNewTabFromSettings(profileGuid, settings);
});
}
// Method Description: // Method Description:
// - Additional responses to clicking on a TabView's item. Currently, just remove tab with middle click // - Additional responses to clicking on a TabView's item. Currently, just remove tab with middle click
// Arguments: // Arguments:

View File

@@ -40,6 +40,9 @@ namespace winrt::TerminalApp::implementation
hstring GetTitle(); hstring GetTitle();
void IncomingConnection(uint64_t serverHandle);
void IncomingConnection(hstring cmdline, hstring workingDir);
// -------------------------------- WinRT Events --------------------------------- // -------------------------------- WinRT Events ---------------------------------
DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs); DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs);
DECLARE_EVENT(LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs); DECLARE_EVENT(LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs);
@@ -100,7 +103,7 @@ namespace winrt::TerminalApp::implementation
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab); void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings); void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings, std::optional<uint64_t> serverHandle = std::nullopt);
void _OpenNewTab(std::optional<int> profileIndex); void _OpenNewTab(std::optional<int> profileIndex);
void _DuplicateTabViewItem(); void _DuplicateTabViewItem();

View File

@@ -30,5 +30,8 @@ namespace TerminalApp
event LastTabClosedEventArgs LastTabClosed; event LastTabClosedEventArgs LastTabClosed;
String GetTitle(); String GetTitle();
void IncomingConnection(UInt64 serverHandle);
void IncomingConnection(String cmdline, String workingDir);
} }
} }

View File

@@ -37,6 +37,23 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
} }
} }
ConhostConnection::ConhostConnection(const uint64_t server,
const uint32_t initialRows,
const uint32_t initialCols,
const guid& initialGuid) :
_initialRows{ initialRows },
_initialCols{ initialCols },
_commandline{},
_startingDirectory{},
_guid{ initialGuid },
_hServer{ reinterpret_cast<HANDLE>(server) }
{
if (_guid == guid{})
{
_guid = Utils::CreateGuid();
}
}
winrt::guid ConhostConnection::Guid() const noexcept winrt::guid ConhostConnection::Guid() const noexcept
{ {
return _guid; return _guid;
@@ -71,6 +88,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
startingDirectory = _startingDirectory; startingDirectory = _startingDirectory;
} }
std::optional<HANDLE> server;
if (_hServer)
{
server = _hServer.get();
}
EnvironmentVariableMapW extraEnvVars; EnvironmentVariableMapW extraEnvVars;
{ {
// Convert connection Guid to string and ignore the enclosing '{}'. // Convert connection Guid to string and ignore the enclosing '{}'.
@@ -88,6 +111,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
startingDirectory, startingDirectory,
static_cast<short>(_initialCols), static_cast<short>(_initialCols),
static_cast<short>(_initialRows), static_cast<short>(_initialRows),
server,
&_inPipe, &_inPipe,
&_outPipe, &_outPipe,
&_signalPipe, &_signalPipe,

View File

@@ -10,6 +10,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
struct ConhostConnection : ConhostConnectionT<ConhostConnection> struct ConhostConnection : ConhostConnectionT<ConhostConnection>
{ {
ConhostConnection(const hstring& cmdline, const hstring& startingDirectory, const uint32_t rows, const uint32_t cols, const guid& guid); ConhostConnection(const hstring& cmdline, const hstring& startingDirectory, const uint32_t rows, const uint32_t cols, const guid& guid);
ConhostConnection(const uint64_t server, const uint32_t rows, const uint32_t cols, const guid& guid);
winrt::event_token TerminalOutput(TerminalConnection::TerminalOutputEventArgs const& handler); winrt::event_token TerminalOutput(TerminalConnection::TerminalOutputEventArgs const& handler);
void TerminalOutput(winrt::event_token const& token) noexcept; void TerminalOutput(winrt::event_token const& token) noexcept;
@@ -41,6 +42,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
wil::unique_handle _hOutputThread; wil::unique_handle _hOutputThread;
wil::unique_process_information _piConhost; wil::unique_process_information _piConhost;
wil::unique_handle _hJob; wil::unique_handle _hJob;
wil::unique_handle _hServer;
static DWORD WINAPI StaticOutputThreadProc(LPVOID lpParameter); static DWORD WINAPI StaticOutputThreadProc(LPVOID lpParameter);
DWORD _OutputThread(); DWORD _OutputThread();

View File

@@ -9,6 +9,7 @@ namespace Microsoft.Terminal.TerminalConnection
runtimeclass ConhostConnection : ITerminalConnection runtimeclass ConhostConnection : ITerminalConnection
{ {
ConhostConnection(String cmdline, String startingDirectory, UInt32 rows, UInt32 columns, Guid guid); ConhostConnection(String cmdline, String startingDirectory, UInt32 rows, UInt32 columns, Guid guid);
ConhostConnection(UInt64 server, UInt32 rows, UInt32 columns, Guid guid);
Guid Guid { get; }; Guid Guid { get; };
}; };

View File

@@ -72,6 +72,16 @@ void AppHost::Initialize()
_window->OnAppInitialized(_app); _window->OnAppInitialized(_app);
} }
void AppHost::IncomingConnectionByHandle(HANDLE handle)
{
_app.IncomingConnection(reinterpret_cast<uint64_t>(handle));
}
void AppHost::IncomingConnectionByLaunch(std::wstring_view cmdline, std::wstring_view workingDir)
{
_app.IncomingConnection(winrt::hstring(cmdline), winrt::hstring(workingDir));
}
// Method Description: // Method Description:
// - Called when the app's title changes. Fires off a window message so we can // - Called when the app's title changes. Fires off a window message so we can
// update the window's title on the main thread. // update the window's title on the main thread.

View File

@@ -14,6 +14,8 @@ public:
AppHost() noexcept; AppHost() noexcept;
virtual ~AppHost(); virtual ~AppHost();
void IncomingConnectionByHandle(HANDLE handle);
void IncomingConnectionByLaunch(std::wstring_view cmdline, std::wstring_view workingDir);
void AppTitleChanged(winrt::hstring newTitle); void AppTitleChanged(winrt::hstring newTitle);
void LastTabClosed(); void LastTabClosed();
void Initialize(); void Initialize();

View File

@@ -79,7 +79,7 @@ void IslandWindow::Close()
// Method Description: // Method Description:
// - Set a callback to be called when we process a WM_CREATE message. This gives // - Set a callback to be called when we process a WM_CREATE message. This gives
// the AppHost a chance to resize the window to the propoer size. // the AppHost a chance to resize the window to the proper size.
// Arguments: // Arguments:
// - pfn: a function to be called during the handling of WM_CREATE. It takes two // - pfn: a function to be called during the handling of WM_CREATE. It takes two
// parameters: // parameters:

View File

@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" /> <Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" /> <Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
<PropertyGroup> <PropertyGroup>
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@@ -79,13 +77,11 @@
<DeploymentContent>true</DeploymentContent> <DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</NativeReferenceFile> </NativeReferenceFile>
<!-- Manually include the xbf files from the app project as content files --> <!-- Manually include the xbf files from the app project as content files -->
<NativeReferenceFile Include="$(OpenConsoleDir)$(Platform)\$(Configuration)\TerminalAppLib\*.xbf"> <NativeReferenceFile Include="$(OpenConsoleDir)$(Platform)\$(Configuration)\TerminalAppLib\*.xbf">
<DeploymentContent>true</DeploymentContent> <DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</NativeReferenceFile> </NativeReferenceFile>
<!-- <!--
the packaging project wont recurse through our dependencies, you have to the packaging project wont recurse through our dependencies, you have to
make sure that if you add a cppwinrt dependency to any of these projects, make sure that if you add a cppwinrt dependency to any of these projects,
@@ -100,15 +96,12 @@
manually add a reference to the TerminalApp winmd and dll below, because we manually add a reference to the TerminalApp winmd and dll below, because we
still need those. --> still need those. -->
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" /> <ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<!-- A small helper for paths to the compiled cppwinrt projects --> <!-- A small helper for paths to the compiled cppwinrt projects -->
<_BinRoot Condition="'$(Platform)' != 'Win32'">$(OpenConsoleDir)$(Platform)\$(Configuration)\</_BinRoot> <_BinRoot Condition="'$(Platform)' != 'Win32'">$(OpenConsoleDir)$(Platform)\$(Configuration)\</_BinRoot>
<_BinRoot Condition="'$(Platform)' == 'Win32'">$(OpenConsoleDir)$(Configuration)\</_BinRoot> <_BinRoot Condition="'$(Platform)' == 'Win32'">$(OpenConsoleDir)$(Configuration)\</_BinRoot>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- Manually reference TerminalApp, since we can't use a ProjectReference. --> <!-- Manually reference TerminalApp, since we can't use a ProjectReference. -->
<Reference Include="TerminalApp"> <Reference Include="TerminalApp">
@@ -118,9 +111,7 @@
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies> <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference> </Reference>
<ReferenceCopyLocalPaths Include="$(_BinRoot)\TerminalApp\TerminalApp.dll" /> <ReferenceCopyLocalPaths Include="$(_BinRoot)\TerminalApp\TerminalApp.dll" />
</ItemGroup> </ItemGroup>
<!-- <!--
This ItemGroup and the Globals PropertyGroup below it are required in order This ItemGroup and the Globals PropertyGroup below it are required in order
to enable F5 debugging for the unpackaged application to enable F5 debugging for the unpackaged application
@@ -136,7 +127,6 @@
<!-- DON'T REDIRECT OUR OUTPUT --> <!-- DON'T REDIRECT OUR OUTPUT -->
<NoOutputRedirection>true</NoOutputRedirection> <NoOutputRedirection>true</NoOutputRedirection>
</PropertyGroup> </PropertyGroup>
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets')" /> <Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>
@@ -148,11 +138,9 @@
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" /> <Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets'))" /> <Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets'))" />
</Target> </Target>
<Import Project="$(OpenConsoleDir)src\common.build.post.props" /> <Import Project="$(OpenConsoleDir)src\common.build.post.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" /> <Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190521.3\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190521.3\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> <Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190521.3\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190521.3\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" /> <Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets')" /> <Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.0-rc\build\native\Microsoft.VCRTForwarders.140.targets')" />
</Project> </Project>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<Manifest Include="WindowsTerminal.manifest" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="AppHost.cpp" />
<ClCompile Include="IslandWindow.cpp" />
<ClCompile Include="NonClientIslandWindow.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="AppHost.h" />
<ClInclude Include="BaseWindow.h" />
<ClInclude Include="IslandWindow.h" />
<ClInclude Include="NonClientIslandWindow.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WindowsTerminal.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -3,6 +3,7 @@
#include "pch.h" #include "pch.h"
#include "AppHost.h" #include "AppHost.h"
#include "..\..\types\Manager.h"
#include "resource.h" #include "resource.h"
using namespace winrt; using namespace winrt;
@@ -107,6 +108,16 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
// provides an implementation of Windows.UI.Xaml.Application. // provides an implementation of Windows.UI.Xaml.Application.
AppHost host; AppHost host;
// Create a manager object for IPC.
Manager manager;
// Create and register on connection callbacks from the manager into the application host.
std::function<void(HANDLE)> onHandleConnection = std::bind(&AppHost::IncomingConnectionByHandle, &host, std::placeholders::_1);
std::function<void(std::wstring_view, std::wstring_view)> onLaunchConnection = std::bind(&AppHost::IncomingConnectionByLaunch, &host, std::placeholders::_1, std::placeholders::_2);
Manager::s_RegisterOnConnection(onHandleConnection);
Manager::s_RegisterOnConnection(onLaunchConnection);
// !!! LOAD BEARING !!! // !!! LOAD BEARING !!!
// This is _magic_. Do the initial loading of our settings *BEFORE* we // This is _magic_. Do the initial loading of our settings *BEFORE* we
// initialize our COM apartment type. This is because the Windows.Storage // initialize our COM apartment type. This is because the Windows.Storage
@@ -132,5 +143,8 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
TranslateMessage(&message); TranslateMessage(&message);
DispatchMessage(&message); DispatchMessage(&message);
} }
manager.NotifyExit();
return 0; return 0;
} }

View File

@@ -20,6 +20,7 @@ const std::wstring_view ConsoleArguments::HEIGHT_ARG = L"--height";
const std::wstring_view ConsoleArguments::INHERIT_CURSOR_ARG = L"--inheritcursor"; const std::wstring_view ConsoleArguments::INHERIT_CURSOR_ARG = L"--inheritcursor";
const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature"; const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature";
const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty"; const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty";
const std::wstring_view ConsoleArguments::FORCE_MANAGER_ARG = L"--manager";
std::wstring EscapeArgument(std::wstring_view ac) std::wstring EscapeArgument(std::wstring_view ac)
{ {
@@ -102,7 +103,9 @@ ConsoleArguments::ConsoleArguments(const std::wstring& commandline,
_vtOutHandle(hStdOut), _vtOutHandle(hStdOut),
_recievedEarlySizeChange{ false }, _recievedEarlySizeChange{ false },
_originalWidth{ -1 }, _originalWidth{ -1 },
_originalHeight{ -1 } _originalHeight{ -1 },
_forceManager{ false },
_workingDirectory{ wil::GetCurrentDirectoryW().get() }
{ {
_clientCommandline = L""; _clientCommandline = L"";
_vtMode = L""; _vtMode = L"";
@@ -285,7 +288,7 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector<std::wstring>& args, _In
// - S_OK if we could successfully parse the given text and store it in the handle value location. // - S_OK if we could successfully parse the given text and store it in the handle value location.
// - E_INVALIDARG if we couldn't parse the text as a valid hex-encoded handle number OR // - E_INVALIDARG if we couldn't parse the text as a valid hex-encoded handle number OR
// if the handle value was already filled. // if the handle value was already filled.
[[nodiscard]] HRESULT ConsoleArguments::s_ParseHandleArg(const std::wstring& handleAsText, _Inout_ DWORD& handleAsVal) [[nodiscard]] HRESULT ConsoleArguments::s_ParseHandleArg(const std::wstring& handleAsText, _Inout_ HANDLE& handleAsVal)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
@@ -296,13 +299,15 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector<std::wstring>& args, _In
} }
else if (0 == handleAsVal) else if (0 == handleAsVal)
{ {
handleAsVal = wcstoul(handleAsText.c_str(), nullptr /*endptr*/, 16 /*base*/); const auto handleAsUlong = wcstoul(handleAsText.c_str(), nullptr /*endptr*/, 16 /*base*/);
// If the handle didn't parse into a reasonable handle ID, invalid. // If the handle didn't parse into a reasonable handle ID, invalid.
if (handleAsVal == 0) if (handleAsUlong == 0)
{ {
hr = E_INVALIDARG; hr = E_INVALIDARG;
} }
handleAsVal = UlongToHandle(handleAsUlong);
} }
else else
{ {
@@ -467,6 +472,12 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector<std::wstring>& args, _In
{ {
hr = s_HandleFeatureValue(args, i); hr = s_HandleFeatureValue(args, i);
} }
else if (arg == FORCE_MANAGER_ARG)
{
_forceManager = true;
s_ConsumeArg(args, i);
hr = S_OK;
}
else if (arg == HEADLESS_ARG) else if (arg == HEADLESS_ARG)
{ {
_headless = true; _headless = true;
@@ -552,62 +563,72 @@ bool ConsoleArguments::InConptyMode() const noexcept
return IsValidHandle(_vtInHandle) || IsValidHandle(_vtOutHandle) || HasSignalHandle(); return IsValidHandle(_vtInHandle) || IsValidHandle(_vtOutHandle) || HasSignalHandle();
} }
bool ConsoleArguments::IsHeadless() const bool ConsoleArguments::IsHeadless() const noexcept
{ {
return _headless; return _headless;
} }
bool ConsoleArguments::ShouldCreateServerHandle() const bool ConsoleArguments::ShouldCreateServerHandle() const noexcept
{ {
return _createServerHandle; return _createServerHandle;
} }
HANDLE ConsoleArguments::GetServerHandle() const bool ConsoleArguments::ShouldSendToManager() const noexcept
{ {
return ULongToHandle(_serverHandle); return _forceManager;
} }
HANDLE ConsoleArguments::GetSignalHandle() const HANDLE ConsoleArguments::GetServerHandle() const noexcept
{ {
return ULongToHandle(_signalHandle); return _serverHandle;
} }
HANDLE ConsoleArguments::GetVtInHandle() const void ConsoleArguments::SetServerHandle(const HANDLE server) noexcept
{
_serverHandle = server;
}
HANDLE ConsoleArguments::GetSignalHandle() const noexcept
{
return _signalHandle;
}
HANDLE ConsoleArguments::GetVtInHandle() const noexcept
{ {
return _vtInHandle; return _vtInHandle;
} }
HANDLE ConsoleArguments::GetVtOutHandle() const HANDLE ConsoleArguments::GetVtOutHandle() const noexcept
{ {
return _vtOutHandle; return _vtOutHandle;
} }
std::wstring ConsoleArguments::GetClientCommandline() const std::wstring_view ConsoleArguments::GetClientCommandline() const noexcept
{ {
return _clientCommandline; return _clientCommandline;
} }
std::wstring ConsoleArguments::GetVtMode() const std::wstring ConsoleArguments::GetVtMode() const noexcept
{ {
return _vtMode; return _vtMode;
} }
bool ConsoleArguments::GetForceV1() const bool ConsoleArguments::GetForceV1() const noexcept
{ {
return _forceV1; return _forceV1;
} }
short ConsoleArguments::GetWidth() const short ConsoleArguments::GetWidth() const noexcept
{ {
return _width; return _width;
} }
short ConsoleArguments::GetHeight() const short ConsoleArguments::GetHeight() const noexcept
{ {
return _height; return _height;
} }
bool ConsoleArguments::GetInheritCursor() const bool ConsoleArguments::GetInheritCursor() const noexcept
{ {
return _inheritCursor; return _inheritCursor;
} }

View File

@@ -33,23 +33,26 @@ public:
bool HasVtHandles() const; bool HasVtHandles() const;
bool InConptyMode() const noexcept; bool InConptyMode() const noexcept;
bool IsHeadless() const; bool IsHeadless() const noexcept;
bool ShouldCreateServerHandle() const; bool ShouldCreateServerHandle() const noexcept;
bool ShouldSendToManager() const noexcept;
HANDLE GetServerHandle() const; HANDLE GetServerHandle() const noexcept;
HANDLE GetVtInHandle() const; void SetServerHandle(const HANDLE server) noexcept;
HANDLE GetVtOutHandle() const;
HANDLE GetVtInHandle() const noexcept;
HANDLE GetVtOutHandle() const noexcept;
bool HasSignalHandle() const; bool HasSignalHandle() const;
HANDLE GetSignalHandle() const; HANDLE GetSignalHandle() const noexcept;
std::wstring GetClientCommandline() const; std::wstring_view GetClientCommandline() const noexcept;
std::wstring GetVtMode() const; std::wstring GetVtMode() const noexcept;
bool GetForceV1() const; bool GetForceV1() const noexcept;
short GetWidth() const; short GetWidth() const noexcept;
short GetHeight() const; short GetHeight() const noexcept;
bool GetInheritCursor() const; bool GetInheritCursor() const noexcept;
void SetExpectedSize(COORD dimensions) noexcept; void SetExpectedSize(COORD dimensions) noexcept;
@@ -66,6 +69,7 @@ public:
static const std::wstring_view INHERIT_CURSOR_ARG; static const std::wstring_view INHERIT_CURSOR_ARG;
static const std::wstring_view FEATURE_ARG; static const std::wstring_view FEATURE_ARG;
static const std::wstring_view FEATURE_PTY_ARG; static const std::wstring_view FEATURE_PTY_ARG;
static const std::wstring_view FORCE_MANAGER_ARG;
private: private:
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
@@ -107,6 +111,8 @@ private:
std::wstring _clientCommandline; std::wstring _clientCommandline;
std::wstring _workingDirectory;
HANDLE _vtInHandle; HANDLE _vtInHandle;
HANDLE _vtOutHandle; HANDLE _vtOutHandle;
@@ -119,9 +125,10 @@ private:
short _width; short _width;
short _height; short _height;
bool _forceManager;
bool _createServerHandle; bool _createServerHandle;
DWORD _serverHandle; HANDLE _serverHandle;
DWORD _signalHandle; HANDLE _signalHandle;
bool _inheritCursor; bool _inheritCursor;
bool _recievedEarlySizeChange; bool _recievedEarlySizeChange;
@@ -144,7 +151,7 @@ private:
_Inout_ size_t& index); _Inout_ size_t& index);
[[nodiscard]] static HRESULT s_ParseHandleArg(const std::wstring& handleAsText, [[nodiscard]] static HRESULT s_ParseHandleArg(const std::wstring& handleAsText,
_Inout_ DWORD& handleAsVal); _Inout_ HANDLE& handle);
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
friend class ConsoleArgumentsTests; friend class ConsoleArgumentsTests;

View File

@@ -70,14 +70,14 @@ VtIo::VtIo() :
return S_OK; return S_OK;
} }
[[nodiscard]] HRESULT VtIo::Initialize(const ConsoleArguments* const pArgs) [[nodiscard]] HRESULT VtIo::Initialize(const ConsoleArguments args)
{ {
_lookingForCursorPosition = pArgs->GetInheritCursor(); _lookingForCursorPosition = args.GetInheritCursor();
// If we were already given VT handles, set up the VT IO engine to use those. // If we were already given VT handles, set up the VT IO engine to use those.
if (pArgs->InConptyMode()) if (args.InConptyMode())
{ {
return _Initialize(pArgs->GetVtInHandle(), pArgs->GetVtOutHandle(), pArgs->GetVtMode(), pArgs->GetSignalHandle()); return _Initialize(args.GetVtInHandle(), args.GetVtOutHandle(), args.GetVtMode(), args.GetSignalHandle());
} }
// Didn't need to initialize if we didn't have VT stuff. It's still OK, but report we did nothing. // Didn't need to initialize if we didn't have VT stuff. It's still OK, but report we did nothing.
else else

View File

@@ -19,7 +19,7 @@ namespace Microsoft::Console::VirtualTerminal
VtIo(); VtIo();
virtual ~VtIo() override = default; virtual ~VtIo() override = default;
[[nodiscard]] HRESULT Initialize(const ConsoleArguments* const pArgs); [[nodiscard]] HRESULT Initialize(const ConsoleArguments args);
[[nodiscard]] HRESULT CreateAndStartSignalThread() noexcept; [[nodiscard]] HRESULT CreateAndStartSignalThread() noexcept;
[[nodiscard]] HRESULT CreateIoHandlers() noexcept; [[nodiscard]] HRESULT CreateIoHandlers() noexcept;

View File

@@ -7,6 +7,7 @@
#include "srvinit.h" #include "srvinit.h"
#include "..\server\Entrypoints.h" #include "..\server\Entrypoints.h"
#include "..\interactivity\inc\ServiceLocator.hpp" #include "..\interactivity\inc\ServiceLocator.hpp"
#include "..\types\Manager.h"
// Define TraceLogging provider // Define TraceLogging provider
TRACELOGGING_DEFINE_PROVIDER( TRACELOGGING_DEFINE_PROVIDER(
@@ -195,7 +196,7 @@ int CALLBACK wWinMain(
{ {
if (args.ShouldCreateServerHandle()) if (args.ShouldCreateServerHandle())
{ {
hr = Entrypoints::StartConsoleForCmdLine(args.GetClientCommandline().c_str(), &args); hr = Entrypoints::StartConsoleForCmdLine(args);
} }
else else
{ {
@@ -203,7 +204,7 @@ int CALLBACK wWinMain(
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = Entrypoints::StartConsoleForServerHandle(args.GetServerHandle(), &args); hr = Entrypoints::StartConsoleForServerHandle(args);
} }
} }
} }

View File

@@ -11,6 +11,7 @@
#include "renderFontDefaults.hpp" #include "renderFontDefaults.hpp"
#include "ApiRoutines.h" #include "ApiRoutines.h"
#include "ConsoleArguments.hpp"
#include "../types/inc/GlyphWidth.hpp" #include "../types/inc/GlyphWidth.hpp"
@@ -31,15 +32,15 @@ using namespace Microsoft::Console::Render;
const UINT CONSOLE_EVENT_FAILURE_ID = 21790; const UINT CONSOLE_EVENT_FAILURE_ID = 21790;
const UINT CONSOLE_LPC_PORT_FAILURE_ID = 21791; const UINT CONSOLE_LPC_PORT_FAILURE_ID = 21791;
[[nodiscard]] HRESULT ConsoleServerInitialization(_In_ HANDLE Server, const ConsoleArguments* const args) [[nodiscard]] HRESULT ConsoleServerInitialization(const ConsoleArguments& args)
{ {
Globals& Globals = ServiceLocator::LocateGlobals(); Globals& Globals = ServiceLocator::LocateGlobals();
try try
{ {
Globals.pDeviceComm = new DeviceComm(Server); Globals.pDeviceComm = new DeviceComm(args.GetServerHandle());
Globals.launchArgs = *args; Globals.launchArgs = args;
Globals.uiOEMCP = GetOEMCP(); Globals.uiOEMCP = GetOEMCP();
Globals.uiWindowsCP = GetACP(); Globals.uiWindowsCP = GetACP();
@@ -245,10 +246,10 @@ void ConsoleCheckDebug()
#endif #endif
} }
[[nodiscard]] HRESULT ConsoleCreateIoThreadLegacy(_In_ HANDLE Server, const ConsoleArguments* const args) [[nodiscard]] HRESULT ConsoleCreateIoThreadLegacy(const ConsoleArguments& args)
{ {
auto& g = ServiceLocator::LocateGlobals(); auto& g = ServiceLocator::LocateGlobals();
RETURN_IF_FAILED(ConsoleServerInitialization(Server, args)); RETURN_IF_FAILED(ConsoleServerInitialization(args));
RETURN_IF_FAILED(g.hConsoleInputInitEvent.create(wil::EventOptions::None)); RETURN_IF_FAILED(g.hConsoleInputInitEvent.create(wil::EventOptions::None));
// Set up and tell the driver about the input available event. // Set up and tell the driver about the input available event.

View File

@@ -21,6 +21,7 @@
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <deque> #include <deque>
#include <future>
#include <list> #include <list>
#include <memory> #include <memory>
#include <map> #include <map>

View File

@@ -201,6 +201,7 @@ bool SignalResizeWindow(const HANDLE hSignal,
// - startingDirectory: The directory to start the process in // - startingDirectory: The directory to start the process in
// - w: The initial width of the pty, in characters // - w: The initial width of the pty, in characters
// - h: The initial height of the pty, in characters // - h: The initial height of the pty, in characters
// - hServer: An optional handle to a server connection handle already established for this PTY.
// - hInput: A handle to the pipe for writing input to the pty. // - hInput: A handle to the pipe for writing input to the pty.
// - hOutput: A handle to the pipe for reading the output of the pty. // - hOutput: A handle to the pipe for reading the output of the pty.
// - hSignal: A handle to the pipe for writing signal messages to the pty. // - hSignal: A handle to the pipe for writing signal messages to the pty.
@@ -216,6 +217,7 @@ bool SignalResizeWindow(const HANDLE hSignal,
std::optional<std::wstring> startingDirectory, std::optional<std::wstring> startingDirectory,
const unsigned short w, const unsigned short w,
const unsigned short h, const unsigned short h,
std::optional<HANDLE> const hServer,
HANDLE* const hInput, HANDLE* const hInput,
HANDLE* const hOutput, HANDLE* const hOutput,
HANDLE* const hSignal, HANDLE* const hSignal,
@@ -254,6 +256,11 @@ bool SignalResizeWindow(const HANDLE hSignal,
SetHandleInformation(outPipeConhostSide, HANDLE_FLAG_INHERIT, 1); SetHandleInformation(outPipeConhostSide, HANDLE_FLAG_INHERIT, 1);
SetHandleInformation(signalPipeConhostSide, HANDLE_FLAG_INHERIT, 1); SetHandleInformation(signalPipeConhostSide, HANDLE_FLAG_INHERIT, 1);
if (hServer.has_value())
{
SetHandleInformation(hServer.value(), HANDLE_FLAG_INHERIT, 1);
}
std::wstring conhostCmdline = L"conhost.exe"; std::wstring conhostCmdline = L"conhost.exe";
conhostCmdline += L" --headless"; conhostCmdline += L" --headless";
std::wstringstream ss; std::wstringstream ss;
@@ -264,9 +271,19 @@ bool SignalResizeWindow(const HANDLE hSignal,
} }
ss << L" --signal 0x" << std::hex << HandleToUlong(signalPipeConhostSide); ss << L" --signal 0x" << std::hex << HandleToUlong(signalPipeConhostSide);
if (hServer.has_value())
{
ss << L" --server 0x" << std::hex << HandleToUlong(hServer.value());
}
conhostCmdline += ss.str(); conhostCmdline += ss.str();
conhostCmdline += L" -- ";
conhostCmdline += cmdline; if (!hServer.has_value())
{
conhostCmdline += L" -- ";
conhostCmdline += cmdline;
}
STARTUPINFO si = { 0 }; STARTUPINFO si = { 0 };
si.cb = sizeof(STARTUPINFOW); si.cb = sizeof(STARTUPINFOW);

View File

@@ -9,10 +9,21 @@
#include "winbasep.h" #include "winbasep.h"
[[nodiscard]] HRESULT Entrypoints::StartConsoleForServerHandle(const HANDLE ServerHandle, #include "..\types\Manager.h"
const ConsoleArguments* const args) #include "..\host\ConsoleArguments.hpp"
[[nodiscard]] HRESULT Entrypoints::StartConsoleForServerHandle(const ConsoleArguments& args)
{ {
return ConsoleCreateIoThreadLegacy(ServerHandle, args); if (args.ShouldSendToManager())
{
if (Manager::s_TrySendToManager(args.GetServerHandle()))
{
SleepEx(INFINITE, FALSE);
return S_OK;
}
}
return ConsoleCreateIoThreadLegacy(args);
} }
// this function has unreachable code due to its unusual lifetime. We // this function has unreachable code due to its unusual lifetime. We
@@ -20,9 +31,17 @@
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4702) #pragma warning(disable : 4702)
[[nodiscard]] HRESULT Entrypoints::StartConsoleForCmdLine(_In_ PCWSTR pwszCmdLine, [[nodiscard]] HRESULT Entrypoints::StartConsoleForCmdLine(ConsoleArguments& args)
const ConsoleArguments* const args)
{ {
if (args.ShouldSendToManager())
{
if (Manager::s_TrySendToManager(args.GetClientCommandline(), L"C:\\windows\\system32"))
{
SleepEx(INFINITE, FALSE);
return S_OK;
}
}
// Create a scope because we're going to exit thread if everything goes well. // Create a scope because we're going to exit thread if everything goes well.
// This scope will ensure all C++ objects and smart pointers get a chance to destruct before ExitThread is called. // This scope will ensure all C++ objects and smart pointers get a chance to destruct before ExitThread is called.
{ {
@@ -39,7 +58,8 @@
L"\\Reference", L"\\Reference",
FALSE)); FALSE));
RETURN_IF_NTSTATUS_FAILED(Entrypoints::StartConsoleForServerHandle(ServerHandle.get(), args)); args.SetServerHandle(ServerHandle.get());
RETURN_IF_NTSTATUS_FAILED(Entrypoints::StartConsoleForServerHandle(args));
// If we get to here, we have transferred ownership of the server handle to the console, so release it. // If we get to here, we have transferred ownership of the server handle to the console, so release it.
// Keep a copy of the value so we can open the client handles even though we're no longer the owner. // Keep a copy of the value so we can open the client handles even though we're no longer the owner.
@@ -143,6 +163,8 @@
NULL)); NULL));
// We have to copy the command line string we're given because CreateProcessW has to be called with mutable data. // We have to copy the command line string we're given because CreateProcessW has to be called with mutable data.
PCWSTR pwszCmdLine = args.GetClientCommandline().data();
if (wcslen(pwszCmdLine) == 0) if (wcslen(pwszCmdLine) == 0)
{ {
// If they didn't give us one, just launch cmd.exe. // If they didn't give us one, just launch cmd.exe.

View File

@@ -20,6 +20,6 @@ class ConsoleArguments;
namespace Entrypoints namespace Entrypoints
{ {
[[nodiscard]] HRESULT StartConsoleForServerHandle(const HANDLE ServerHandle, const ConsoleArguments* const args); [[nodiscard]] HRESULT StartConsoleForServerHandle(const ConsoleArguments& args);
[[nodiscard]] HRESULT StartConsoleForCmdLine(_In_ PCWSTR pwszCmdLine, const ConsoleArguments* const args); [[nodiscard]] HRESULT StartConsoleForCmdLine(ConsoleArguments& args);
}; };

View File

@@ -5,4 +5,4 @@
class ConsoleArguments; class ConsoleArguments;
[[nodiscard]] HRESULT ConsoleCreateIoThreadLegacy(_In_ HANDLE Server, const ConsoleArguments* const args); [[nodiscard]] HRESULT ConsoleCreateIoThreadLegacy(const ConsoleArguments& args);

357
src/types/Manager.cpp Normal file
View File

@@ -0,0 +1,357 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "Manager.h"
static constexpr std::wstring_view MUTEX_NAME = L"Local\\WindowsTerminalManager";
static constexpr std::wstring_view PIPE_NAME = L"\\\\.\\pipe\\WindowsTerminalManagerPipe";
static constexpr DWORD PIPE_BUFFER_SIZE = 4096;
Manager::Manager() :
_mutex(),
_pipe(),
_exit(),
_theServer(false)
{
// Create exit event.
_exit.create();
// Try to open the mutex.
if (!_mutex.try_open(MUTEX_NAME.data()))
{
// If we can't open it, create one because no one else did.
_mutex.create(MUTEX_NAME.data());
_theServer = true;
}
else
{
// If it could just be opened, we're not the server.
_theServer = false;
}
// If we're the server, establish communication listener and thread
if (_theServer)
{
_becomeServer();
}
// If we're not the server, find out who it is so we can get notified when they leave and become the server.
else
{
ManagerMessageQuery query;
query.size = sizeof(query);
query.type = ManagerMessageTypes::GetManagerPid;
const auto reply = _ask(query);
_waitToBecomeServer = std::async(std::launch::async, [reply, this] {
wil::unique_handle managerProcess(OpenProcess(SYNCHRONIZE, FALSE, reply.reply.getPid.id));
THROW_LAST_ERROR_IF_NULL(managerProcess.get());
WaitForSingleObject(managerProcess.get(), INFINITE);
_becomeServer();
});
}
}
Manager::~Manager()
{
}
void Manager::NotifyExit()
{
_exit.SetEvent();
}
bool Manager::s_TrySendToManager(const HANDLE server)
{
try
{
// Get PID from remote process.
ManagerMessageQuery query;
query.size = sizeof(query);
query.type = ManagerMessageTypes::GetManagerPid;
auto reply = _ask(query);
const auto processId = reply.reply.getPid.id;
// Open for handle duping.
wil::unique_handle otherProcess(OpenProcess(PROCESS_DUP_HANDLE, FALSE, processId));
THROW_LAST_ERROR_IF_NULL(otherProcess.get());
// Send handle into that process.
HANDLE targetHandle;
THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), server,
otherProcess.get(), &targetHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS));
// Tell remote process about new handle ID
query.size = sizeof(query);
query.type = ManagerMessageTypes::SendConnection;
query.query.sendConn.handle = targetHandle;
reply = _ask(query);
return reply.reply.sendConn.ok;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}
}
bool Manager::s_TrySendToManager(const std::wstring_view cmdline,
const std::wstring_view workingDir)
{
try
{
// Figure out how much data we actually need to send with the variable length strings
size_t size = sizeof(ManagerMessageQuery) + 1;
size += 2*(cmdline.size() + 1);
size += 2*(workingDir.size() + 1);
// Make a big buffer to hold it all contiguously
const auto buffer = std::make_unique<byte[]>(size);
// Make pointers to fill up each piece of data
const auto query = reinterpret_cast<ManagerMessageQuery*>(buffer.get());
const auto cmdPayload = reinterpret_cast<PWCHAR>(buffer.get() + sizeof(ManagerMessageQuery) + 1);
const auto workingPayload = reinterpret_cast<PWCHAR>(buffer.get() + sizeof(ManagerMessageQuery) + 1 + (cmdline.size() + 1) * 2);
// Tell the remote process the command line and working directory
query->size = gsl::narrow<DWORD>(size);
query->type = ManagerMessageTypes::SendCmdAndWorking;
query->query.sendCmdAndWorking.cmd = gsl::narrow<DWORD>(cmdline.size());
query->query.sendCmdAndWorking.working = gsl::narrow<DWORD>(workingDir.size());
THROW_IF_FAILED(StringCchCopyW(cmdPayload, cmdline.size() + 1, cmdline.data()));
THROW_IF_FAILED(StringCchCopyW(workingPayload, workingDir.size() + 1, workingDir.data()));
const auto reply = _ask(*query);
return reply.reply.sendConn.ok;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}
}
void Manager::_becomeServer()
{
// lock?
// test if pipe exists?
_theServer = true;
// enter loop to make server connections
_serverWork = std::async(std::launch::async, [this] {
_serverLoop();
});
}
void Manager::_serverLoop()
{
wil::unique_event newClient;
newClient.create(wil::EventOptions::ManualReset);
bool keepGoing = true;
while (keepGoing)
{
OVERLAPPED overlap = { 0 };
overlap.hEvent = newClient.get();
wil::unique_handle pipe(CreateNamedPipeW(PIPE_NAME.data(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_ACCEPT_REMOTE_CLIENTS,
PIPE_UNLIMITED_INSTANCES,
PIPE_BUFFER_SIZE,
PIPE_BUFFER_SIZE,
0,
nullptr)); // replace with actual security descriptor?
THROW_LAST_ERROR_IF(pipe.get() == INVALID_HANDLE_VALUE);
if (!ConnectNamedPipe(pipe.get(), &overlap))
{
// IO pending and Pipe Connected are OK error codes. Go into the wait.
const auto gle = GetLastError();
if (gle != ERROR_IO_PENDING && gle != ERROR_PIPE_CONNECTED)
{
THROW_LAST_ERROR();
}
}
std::array<HANDLE, 2> waitOn;
waitOn.at(0) = _exit.get();
waitOn.at(1) = newClient.get();
const auto ret = WaitForMultipleObjects(gsl::narrow<DWORD>(waitOn.size()), waitOn.data(), FALSE, INFINITE);
if (ret == WAIT_OBJECT_0)
{
keepGoing = false;
continue;
}
else if (ret == WAIT_OBJECT_0 + 1)
{
const auto loosePipeHandle = pipe.release();
auto future = std::async(std::launch::async, [this, loosePipeHandle] {
_perClientLoop(wil::unique_handle(loosePipeHandle));
});
_perClientWork.push_back(std::move(future));
}
else if (ret == WAIT_FAILED)
{
THROW_LAST_ERROR();
}
else
{
THROW_WIN32(ret);
}
}
}
void Manager::_perClientLoop(wil::unique_handle pipe)
{
/*bool keepGoing = true;
while (keepGoing)*/
{
ManagerMessageQuery query;
DWORD bytesRead = 0;
SetLastError(S_OK);
const auto result = ReadFile(pipe.get(),
&query,
sizeof(query),
&bytesRead,
nullptr);
// False is OK if it's ERROR_MORE_DATA.
const auto gle = GetLastError();
THROW_LAST_ERROR_IF(!result && gle != ERROR_MORE_DATA);
std::unique_ptr<byte[]> buffer;
if (gle == ERROR_MORE_DATA)
{
const auto remainingBytes = query.size - gsl::narrow<DWORD>(sizeof(query));
buffer = std::make_unique<byte[]>(remainingBytes);
bytesRead = 0;
THROW_IF_WIN32_BOOL_FALSE(ReadFile(pipe.get(),
buffer.get(),
remainingBytes,
&bytesRead,
nullptr));
}
ManagerMessageReply reply;
switch (query.type)
{
case ManagerMessageTypes::GetManagerPid:
reply = _getPid(query);
break;
case ManagerMessageTypes::SendConnection:
reply = _sendConnection(query);
break;
case ManagerMessageTypes::SendCmdAndWorking:
reply = _sendCmdAndWorking(query, buffer);
break;
default:
THROW_HR(E_NOTIMPL);
}
DWORD bytesWritten = 0;
THROW_IF_WIN32_BOOL_FALSE(WriteFile(pipe.get(),
&reply,
sizeof(reply),
&bytesWritten,
nullptr));
}
}
Manager::ManagerMessageReply Manager::_ask(Manager::ManagerMessageQuery& query)
{
ManagerMessageReply reply;
DWORD bytesRead = 0;
THROW_IF_WIN32_BOOL_FALSE(CallNamedPipeW(PIPE_NAME.data(),
&query,
query.size,
&reply,
sizeof(reply),
&bytesRead,
NMPWAIT_WAIT_FOREVER));
return reply;
}
Manager::ManagerMessageReply Manager::_getPid(ManagerMessageQuery /*query*/)
{
ManagerMessageReply reply;
reply.type = ManagerMessageTypes::GetManagerPid;
reply.reply.getPid.id = GetCurrentProcessId();
return reply;
}
static std::vector<std::function<void(HANDLE)>> s_onHandleConnection;
void Manager::s_RegisterOnConnection(std::function<void(HANDLE)> func)
{
s_onHandleConnection.push_back(func);
}
Manager::ManagerMessageReply Manager::_sendConnection(ManagerMessageQuery query)
{
const auto serverHandle = query.query.sendConn.handle;
// create new conhost connection...
for (const auto& func : s_onHandleConnection)
{
func(serverHandle);
}
ManagerMessageReply reply;
reply.type = ManagerMessageTypes::SendConnection;
reply.reply.sendConn.ok = true;
return reply;
}
static std::vector<std::function<void(std::wstring_view, std::wstring_view)>> s_onLaunchConnection;
void Manager::s_RegisterOnConnection(std::function<void(std::wstring_view, std::wstring_view)> func)
{
s_onLaunchConnection.push_back(func);
}
Manager::ManagerMessageReply Manager::_sendCmdAndWorking(ManagerMessageQuery query, std::unique_ptr<BYTE[]>& buffer)
{
const auto cmd = query.query.sendCmdAndWorking.cmd;
const auto work = query.query.sendCmdAndWorking.working;
std::wstring_view cmdline(reinterpret_cast<wchar_t*>(buffer.get() + 1), cmd);
std::wstring_view workingDir(reinterpret_cast<wchar_t*>(buffer.get() + 1 + (cmd + 1) * 2), work);
// create new conhost connection...
for (const auto& func : s_onLaunchConnection)
{
func(cmdline, workingDir);
}
ManagerMessageReply reply;
reply.type = ManagerMessageTypes::SendCmdAndWorking;
reply.reply.sendCmdAndWorking.ok = true;
return reply;
}

84
src/types/Manager.h Normal file
View File

@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
class Manager final
{
public:
Manager();
virtual ~Manager();
void NotifyExit();
static void s_RegisterOnConnection(std::function<void(HANDLE)> func);
static void s_RegisterOnConnection(std::function<void(std::wstring_view, std::wstring_view)> func);
static bool s_TrySendToManager(const HANDLE server);
static bool s_TrySendToManager(const std::wstring_view cmdline,
const std::wstring_view workingDir);
Manager(const Manager&) = delete;
Manager(Manager&&) = delete;
Manager& operator=(const Manager&) = delete;
Manager& operator=(Manager&&) = delete;
private:
wil::unique_mutex _mutex;
wil::unique_event _exit;
wil::unique_handle _pipe;
bool _theServer;
std::future<void> _waitToBecomeServer;
std::future<void> _serverWork;
std::vector<std::future<void>> _perClientWork;
enum class ManagerMessageTypes
{
GetManagerPid,
SendConnection,
SendCmdAndWorking
};
struct ManagerMessageQuery
{
DWORD size;
ManagerMessageTypes type;
union
{
struct SendConnection
{
HANDLE handle;
} sendConn;
struct SendCmdAndWorking
{
DWORD cmd;
DWORD working;
} sendCmdAndWorking;
} query;
};
struct ManagerMessageReply
{
ManagerMessageTypes type;
union
{
struct GetManagerPid
{
DWORD id;
} getPid;
struct SendConnection
{
bool ok;
} sendConn;
struct SendCmdAndWorking
{
bool ok;
} sendCmdAndWorking;
} reply;
};
static ManagerMessageReply _ask(ManagerMessageQuery& query);
static ManagerMessageReply _getPid(ManagerMessageQuery query);
static ManagerMessageReply _sendConnection(ManagerMessageQuery query);
static ManagerMessageReply _sendCmdAndWorking(ManagerMessageQuery query, std::unique_ptr<byte[]>& buffer);
void _becomeServer();
void _serverLoop();
void _perClientLoop(wil::unique_handle pipe);
};

View File

@@ -5,6 +5,7 @@
<ClCompile Include="..\CodepointWidthDetector.cpp" /> <ClCompile Include="..\CodepointWidthDetector.cpp" />
<ClCompile Include="..\convert.cpp" /> <ClCompile Include="..\convert.cpp" />
<ClCompile Include="..\GlyphWidth.cpp" /> <ClCompile Include="..\GlyphWidth.cpp" />
<ClCompile Include="..\Manager.cpp" />
<ClCompile Include="..\MouseEvent.cpp" /> <ClCompile Include="..\MouseEvent.cpp" />
<ClCompile Include="..\FocusEvent.cpp" /> <ClCompile Include="..\FocusEvent.cpp" />
<ClCompile Include="..\IInputEvent.cpp" /> <ClCompile Include="..\IInputEvent.cpp" />
@@ -26,6 +27,7 @@
<ClInclude Include="..\inc\IInputEvent.hpp" /> <ClInclude Include="..\inc\IInputEvent.hpp" />
<ClInclude Include="..\inc\Viewport.hpp" /> <ClInclude Include="..\inc\Viewport.hpp" />
<ClInclude Include="..\inc\Utf16Parser.hpp" /> <ClInclude Include="..\inc\Utf16Parser.hpp" />
<ClInclude Include="..\Manager.h" />
<ClInclude Include="..\precomp.h" /> <ClInclude Include="..\precomp.h" />
<ClInclude Include="..\utils.hpp" /> <ClInclude Include="..\utils.hpp" />
</ItemGroup> </ItemGroup>
@@ -39,4 +41,4 @@
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. --> <!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.lib.props" /> <Import Project="$(SolutionDir)src\common.build.lib.props" />
<Import Project="$(SolutionDir)src\common.build.post.props" /> <Import Project="$(SolutionDir)src\common.build.post.props" />
</Project> </Project>

View File

@@ -57,6 +57,9 @@
<ClCompile Include="..\utils.cpp"> <ClCompile Include="..\utils.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Manager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\inc\IInputEvent.hpp"> <ClInclude Include="..\inc\IInputEvent.hpp">
@@ -83,6 +86,9 @@
<ClInclude Include="..\utils.hpp"> <ClInclude Include="..\utils.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Manager.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" /> <Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />

View File

@@ -33,6 +33,7 @@ SOURCES= \
..\FocusEvent.cpp \ ..\FocusEvent.cpp \
..\GlyphWidth.cpp \ ..\GlyphWidth.cpp \
..\KeyEvent.cpp \ ..\KeyEvent.cpp \
..\Manager.cpp \
..\MenuEvent.cpp \ ..\MenuEvent.cpp \
..\ModifierKeyState.cpp \ ..\ModifierKeyState.cpp \
..\MouseEvent.cpp \ ..\MouseEvent.cpp \