terminal/src/server/Entrypoints.cpp
pi1024e 9f95b54f2c
Change NULL to nullptr since they are pointers (#4960)
Some functions and variables are having NULL assigned to them when they are in fact pointers, so nullptr might be more accurate here.
2020-03-20 20:35:12 +00:00

180 lines
9.3 KiB
C++

// 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);
// 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)