terminal/src/server/Entrypoints.cpp

180 lines
9.3 KiB
C++
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "Entrypoints.h"
#include "DeviceHandle.h"
#include "IoThread.h"
#include "winbasep.h"
[[nodiscard]] HRESULT Entrypoints::StartConsoleForServerHandle(const HANDLE ServerHandle,
const ConsoleArguments* const args)
{
return ConsoleCreateIoThreadLegacy(ServerHandle, args);
}
// this function has unreachable code due to its unusual lifetime. We
// disable the warning about it here.
#pragma warning(push)
#pragma warning(disable : 4702)
[[nodiscard]] HRESULT Entrypoints::StartConsoleForCmdLine(_In_ PCWSTR pwszCmdLine,
const ConsoleArguments* const args)
{
// 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.
{
// TODO:MSFT:13271366 use the arguments from the commandline to determine if we need
// to create the server handle or not.
// Create the server and reference handles and create the console object.
wil::unique_handle ServerHandle;
RETURN_IF_NTSTATUS_FAILED(DeviceHandle::CreateServerHandle(ServerHandle.addressof(), FALSE));
wil::unique_handle ReferenceHandle;
RETURN_IF_NTSTATUS_FAILED(DeviceHandle::CreateClientHandle(ReferenceHandle.addressof(),
ServerHandle.get(),
L"\\Reference",
FALSE));
RETURN_IF_NTSTATUS_FAILED(Entrypoints::StartConsoleForServerHandle(ServerHandle.get(), args));
// 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.
HANDLE const hServer = ServerHandle.release();
// Now that the console object was created, we're in a state that lets us
// create the default io objects.
wil::unique_handle ClientHandle[3];
// Input
RETURN_IF_NTSTATUS_FAILED(DeviceHandle::CreateClientHandle(ClientHandle[0].addressof(),
hServer,
L"\\Input",
TRUE));
// Output
RETURN_IF_NTSTATUS_FAILED(DeviceHandle::CreateClientHandle(ClientHandle[1].addressof(),
hServer,
L"\\Output",
TRUE));
// Error is a copy of Output
RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(),
ClientHandle[1].get(),
GetCurrentProcess(),
ClientHandle[2].addressof(),
0,
TRUE,
DUPLICATE_SAME_ACCESS));
// Create the child process. We will temporarily overwrite the values in the
// PEB to force them to be inherited.
STARTUPINFOEX StartupInformation = { 0 };
StartupInformation.StartupInfo.cb = sizeof(STARTUPINFOEX);
StartupInformation.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
StartupInformation.StartupInfo.hStdInput = ClientHandle[0].get();
StartupInformation.StartupInfo.hStdOutput = ClientHandle[1].get();
StartupInformation.StartupInfo.hStdError = ClientHandle[2].get();
// Get the parent startup info for this process. It might contain LNK data we need to pass to the child.
{
STARTUPINFO HostStartupInfo = { 0 };
HostStartupInfo.cb = sizeof(STARTUPINFO);
GetStartupInfoW(&HostStartupInfo);
A better fix for #tab-titles-are-too-long (#2373) ### User Stories: 1. A user wants to be able to use the executable path as their starting title - Does anyone want this? 2. A user wants to be able to set a custom starting title, but have that title be overridable 3. A user wants to be able to set an overridable starting title, different from the profile name - Presumably someone will want this 4. A user totally wants to ignore the VT title and use something else - This will make more sense in the post [#1320] "Support runtime variables in the custom user title" settings ### Solutions: 1. `name`, `startingTitle`, `tabTitle` * a. `name` is only ever used as the profile name. * b. If `startingTitle` isn't set, then the executable path is used * c. If `startingTitle` is set, it's used as the initial title * d. If `tabTitle` is set, it overrides the title from the terminal * e. Current users of `tabTitle` need to manually update to the new behavior. 2. `name` as starting title, `tabTitle` as a different starting title * a. `name` is used as the starting title and the profile name in the dropdown * b. If `tabTitle` is set, we'll use that as the overridable starting title instead. * c. In the future, `dynamicTabTitle` or `tabTitleOverride` could be added to support [#1320] * d. Current users of `tabTitle` automatically get the new (different!) behavior. * e. User Story 1 is impossible - Does anyone want the behavior _ever_? Perhaps making that scenario impossible is good? 3. `name` unchanged, `tabTitle` as the starting title * a. `name` is only ever used as the profile name. * b. If `tabTitle` is set, we'll use that as the overridable starting title. * c. In the future, `dynamicTabTitle` or `tabTitleOverride` could be added to support [#1320] * d. Current users of `tabTitle` automatically get the new (different!) behavior. 4. `name` as starting title, `tabTitle` as different starting title, `suppressApplicationTitle` Boolean to force it to override * a. `name`, `tabTitle` work as in Solution 2. * b. When someone wants to be able to statically totally override that title (story 4), they can use `suppressApplicationTitle` * c. `suppressApplicationTitle` name is WIP * d. We'll add `suppressApplicationTitle` when someone complains * e. If you really want story 1, use `tabTitle: c:\path\to\foo.exe` and `suppressApplicationTitle`. [#1320]: https://github.com/microsoft/terminal/issues/1320 We've decided to pursue path 4.
2019-08-15 01:16:38 +02:00
// Pass the title we were started with down to our child process.
// Conhost itself absolutely doesn't care about this value, but the
// child might.
StartupInformation.StartupInfo.lpTitle = HostStartupInfo.lpTitle;
// If we were started with Title is Link Name, then pass the flag
// down to the child. (the link name was already passed down above)
if (WI_IsFlagSet(HostStartupInfo.dwFlags, STARTF_TITLEISLINKNAME))
{
StartupInformation.StartupInfo.dwFlags |= STARTF_TITLEISLINKNAME;
}
}
// Create the extended attributes list that will pass the console server information into the child process.
// Call first time to find size
SIZE_T AttributeListSize;
InitializeProcThreadAttributeList(nullptr,
2,
0,
&AttributeListSize);
// Alloc space
wistd::unique_ptr<BYTE[]> AttributeList = wil::make_unique_nothrow<BYTE[]>(AttributeListSize);
RETURN_IF_NULL_ALLOC(AttributeList);
StartupInformation.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(AttributeList.get());
// Call second time to actually initialize space.
RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(StartupInformation.lpAttributeList,
2, // This represents the length of the list. We will call UpdateProcThreadAttribute twice so this is 2.
0,
&AttributeListSize));
// Set cleanup data for ProcThreadAttributeList when successful.
auto CleanupProcThreadAttribute = wil::scope_exit([&] {
DeleteProcThreadAttributeList(StartupInformation.lpAttributeList);
});
RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(StartupInformation.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_CONSOLE_REFERENCE,
ReferenceHandle.addressof(),
sizeof(HANDLE),
NULL,
NULL));
// UpdateProcThreadAttributes wants this as a bare array of handles and doesn't like our smart structures,
// so set it up for its use.
HANDLE HandleList[3];
HandleList[0] = StartupInformation.StartupInfo.hStdInput;
HandleList[1] = StartupInformation.StartupInfo.hStdOutput;
HandleList[2] = StartupInformation.StartupInfo.hStdError;
RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(StartupInformation.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
&HandleList[0],
sizeof HandleList,
NULL,
NULL));
// We have to copy the command line string we're given because CreateProcessW has to be called with mutable data.
if (wcslen(pwszCmdLine) == 0)
{
// If they didn't give us one, just launch cmd.exe.
pwszCmdLine = L"%WINDIR%\\system32\\cmd.exe";
}
// Expand any environment variables present in the command line string.
std::wstring cmdLine = wil::ExpandEnvironmentStringsW<std::wstring>(pwszCmdLine);
// Call create process
wil::unique_process_information ProcessInformation;
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(NULL,
cmdLine.data(),
NULL,
NULL,
TRUE,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
&StartupInformation.StartupInfo,
ProcessInformation.addressof()));
}
// Exit the thread so the CRT won't clean us up and kill. The IO thread owns the lifetime now.
ExitThread(S_OK);
// We won't hit this. The ExitThread above will kill the caller at this point.
FAIL_FAST_HR(E_UNEXPECTED);
return S_OK;
}
#pragma warning(pop)