Merge commit 'refs/pull/11390/merge' of https://github.com/microsoft/terminal into dev/miniksa/selfhost-1.12
This commit is contained in:
commit
d96d72dc03
6
.github/actions/spelling/expect/expect.txt
vendored
6
.github/actions/spelling/expect/expect.txt
vendored
|
@ -189,13 +189,12 @@ cacafire
|
|||
callee
|
||||
capslock
|
||||
CARETBLINKINGENABLED
|
||||
carlos
|
||||
CARRIAGERETURN
|
||||
cascadia
|
||||
cassert
|
||||
castsi
|
||||
catid
|
||||
carlos
|
||||
zamora
|
||||
cazamor
|
||||
CBash
|
||||
cbegin
|
||||
|
@ -1232,6 +1231,7 @@ KLF
|
|||
KLMNO
|
||||
KLMNOPQRST
|
||||
KLMNOPQRSTQQQQQ
|
||||
KPRIORITY
|
||||
KVM
|
||||
langid
|
||||
LANGUAGELIST
|
||||
|
@ -1803,6 +1803,7 @@ POSX
|
|||
POSXSCROLL
|
||||
POSYSCROLL
|
||||
ppci
|
||||
PPEB
|
||||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
|
@ -2850,6 +2851,7 @@ YSize
|
|||
YSubstantial
|
||||
YVIRTUALSCREEN
|
||||
YWalk
|
||||
zamora
|
||||
ZCmd
|
||||
ZCtrl
|
||||
zsh
|
||||
|
|
|
@ -218,7 +218,7 @@ namespace winrt::TerminalApp::implementation
|
|||
_RegisterActionCallbacks();
|
||||
|
||||
// Hook up inbound connection event handler
|
||||
TerminalConnection::ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
|
||||
ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
|
||||
|
||||
//Event Bindings (Early)
|
||||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
|
@ -2700,38 +2700,13 @@ namespace winrt::TerminalApp::implementation
|
|||
return _isAlwaysOnTop;
|
||||
}
|
||||
|
||||
HRESULT TerminalPage::_OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection)
|
||||
HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection)
|
||||
{
|
||||
// We need to be on the UI thread in order for _OpenNewTab to run successfully.
|
||||
// HasThreadAccess will return true if we're currently on a UI thread and false otherwise.
|
||||
// When we're on a COM thread, we'll need to dispatch the calls to the UI thread
|
||||
// and wait on it hence the locking mechanism.
|
||||
if (Dispatcher().HasThreadAccess())
|
||||
{
|
||||
try
|
||||
{
|
||||
NewTerminalArgs newTerminalArgs{};
|
||||
// TODO GH#10952: When we pass the actual commandline (or originating application), the
|
||||
// settings model can choose the right settings based on command matching, or synthesize
|
||||
// a profile from the registry/link settings (TODO GH#9458).
|
||||
// TODO GH#9458: Get and pass the LNK/EXE filenames.
|
||||
// Passing in a commandline forces GetProfileForArgs to use Base Layer instead of Default Profile;
|
||||
// in the future, it can make a better decision based on the value we pull out of the process handle.
|
||||
// TODO GH#5047: When we hang on to the N.T.A., try not to spawn "default... .exe" :)
|
||||
newTerminalArgs.Commandline(L"default-terminal-invocation-placeholder");
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, connection);
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
if (!Dispatcher().HasThreadAccess())
|
||||
{
|
||||
til::latch latch{ 1 };
|
||||
HRESULT finalVal = S_OK;
|
||||
|
@ -2739,13 +2714,27 @@ namespace winrt::TerminalApp::implementation
|
|||
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
|
||||
// Re-running ourselves under the dispatcher will cause us to take the first branch above.
|
||||
finalVal = _OnNewConnection(connection);
|
||||
|
||||
latch.count_down();
|
||||
});
|
||||
|
||||
latch.wait();
|
||||
return finalVal;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
NewTerminalArgs newTerminalArgs;
|
||||
newTerminalArgs.Commandline(connection.Commandline());
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, connection);
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -388,7 +388,7 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::Microsoft::Terminal::Settings::Model::Command _lastPreviewedCommand{ nullptr };
|
||||
std::vector<std::function<void()>> _restorePreviewFuncs{};
|
||||
|
||||
HRESULT _OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection);
|
||||
HRESULT _OnNewConnection(const winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection& connection);
|
||||
void _HandleToggleInboundPty(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
|
||||
void _WindowRenamerActionClick(const IInspectable& sender, const IInspectable& eventArgs);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "ConptyConnection.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <userenv.h>
|
||||
#include <UserEnv.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#include "ConptyConnection.g.cpp"
|
||||
#include "CTerminalHandoff.h"
|
||||
|
@ -276,24 +276,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
const HANDLE hClientProcess) :
|
||||
_initialRows{ 25 },
|
||||
_initialCols{ 80 },
|
||||
_commandline{ L"" },
|
||||
_startingDirectory{ L"" },
|
||||
_startingTitle{ L"" },
|
||||
_environment{ nullptr },
|
||||
_guid{},
|
||||
_u8State{},
|
||||
_u16Str{},
|
||||
_buffer{},
|
||||
_inPipe{ hIn },
|
||||
_outPipe{ hOut }
|
||||
{
|
||||
THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC));
|
||||
if (_guid == guid{})
|
||||
{
|
||||
_guid = Utils::CreateGuid();
|
||||
}
|
||||
|
||||
_piClient.hProcess = hClientProcess;
|
||||
|
||||
try
|
||||
{
|
||||
_commandline = _commandlineFromProcess(hClientProcess);
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
|
@ -355,6 +348,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
return _guid;
|
||||
}
|
||||
|
||||
winrt::hstring ConptyConnection::Commandline() const
|
||||
{
|
||||
return _commandline;
|
||||
}
|
||||
|
||||
void ConptyConnection::Start()
|
||||
try
|
||||
{
|
||||
|
@ -560,6 +558,41 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
}
|
||||
CATCH_LOG()
|
||||
|
||||
// Returns the command line of the given process.
|
||||
// Requires PROCESS_BASIC_INFORMATION | PROCESS_VM_READ privileges.
|
||||
winrt::hstring ConptyConnection::_commandlineFromProcess(HANDLE process)
|
||||
{
|
||||
// I know MSDN documents NtQueryInformationProcess with Reserved1/2/3
|
||||
// fields, but... uh... that feels like security by obfuscation.
|
||||
// .NET kindly published this struct before us.
|
||||
struct PROCESS_BASIC_INFORMATION
|
||||
{
|
||||
NTSTATUS ExitStatus;
|
||||
PPEB PebBaseAddress;
|
||||
ULONG_PTR AffinityMask;
|
||||
KPRIORITY BasePriority;
|
||||
ULONG_PTR UniqueProcessId;
|
||||
ULONG_PTR InheritedFromUniqueProcessId;
|
||||
} info;
|
||||
THROW_IF_NTSTATUS_FAILED(NtQueryInformationProcess(process, ProcessBasicInformation, &info, sizeof(info), nullptr));
|
||||
|
||||
// PEB: Process Environment Block
|
||||
// This is a funny structure allocated by the kernel which contains all sorts of useful
|
||||
// information, only a tiny fraction of which are documented publicly unfortunately.
|
||||
// Fortunately however it contains a copy of the command line the process launched with.
|
||||
PEB peb;
|
||||
THROW_IF_WIN32_BOOL_FALSE(ReadProcessMemory(process, info.PebBaseAddress, &peb, sizeof(peb), nullptr));
|
||||
|
||||
RTL_USER_PROCESS_PARAMETERS params;
|
||||
THROW_IF_WIN32_BOOL_FALSE(ReadProcessMemory(process, peb.ProcessParameters, ¶ms, sizeof(params), nullptr));
|
||||
|
||||
// Yeah I know... Don't use "impl" stuff... But why do you make something _that_ useful private? :(
|
||||
// The hstring_builder allows us to create a hstring without intermediate copies. Neat!
|
||||
winrt::impl::hstring_builder commandline{ params.CommandLine.Length / 2u };
|
||||
THROW_IF_WIN32_BOOL_FALSE(ReadProcessMemory(process, params.CommandLine.Buffer, commandline.data(), params.CommandLine.Length, nullptr));
|
||||
return commandline.to_hstring();
|
||||
}
|
||||
|
||||
DWORD ConptyConnection::_OutputThread()
|
||||
{
|
||||
// Keep us alive until the output thread terminates; the destructor
|
||||
|
@ -636,8 +669,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept
|
||||
try
|
||||
{
|
||||
auto conn = winrt::make<implementation::ConptyConnection>(signal, in, out, ref, server, client);
|
||||
_newConnectionHandlers(conn);
|
||||
_newConnectionHandlers(winrt::make<ConptyConnection>(signal, in, out, ref, server, client));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
void ClearBuffer();
|
||||
|
||||
winrt::guid Guid() const noexcept;
|
||||
winrt::hstring Commandline() const;
|
||||
|
||||
static void StartInboundListener();
|
||||
static void StopInboundListener();
|
||||
|
@ -56,12 +57,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
|
||||
|
||||
private:
|
||||
static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept;
|
||||
static winrt::hstring _commandlineFromProcess(HANDLE process);
|
||||
|
||||
HRESULT _LaunchAttachedClient() noexcept;
|
||||
void _indicateExitWithStatus(unsigned int status) noexcept;
|
||||
void _ClientTerminated() noexcept;
|
||||
|
||||
static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept;
|
||||
|
||||
uint32_t _initialRows{};
|
||||
uint32_t _initialCols{};
|
||||
hstring _commandline{};
|
||||
|
|
|
@ -5,10 +5,13 @@ import "ITerminalConnection.idl";
|
|||
|
||||
namespace Microsoft.Terminal.TerminalConnection
|
||||
{
|
||||
delegate void NewConnectionHandler(ConptyConnection connection);
|
||||
|
||||
[default_interface] runtimeclass ConptyConnection : ITerminalConnection
|
||||
{
|
||||
ConptyConnection();
|
||||
Guid Guid { get; };
|
||||
String Commandline { get; };
|
||||
void ClearBuffer();
|
||||
|
||||
static event NewConnectionHandler NewConnection;
|
||||
|
|
|
@ -29,6 +29,4 @@ namespace Microsoft.Terminal.TerminalConnection
|
|||
event Windows.Foundation.TypedEventHandler<ITerminalConnection, Object> StateChanged;
|
||||
ConnectionState State { get; };
|
||||
};
|
||||
|
||||
delegate void NewConnectionHandler(ITerminalConnection connection);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include <LibraryResources.h>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
|
@ -529,10 +532,13 @@ Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs&
|
|||
{
|
||||
if (newTerminalArgs)
|
||||
{
|
||||
if (auto profile = GetProfileByName(newTerminalArgs.Profile()))
|
||||
if (const auto name = newTerminalArgs.Profile(); !name.empty())
|
||||
{
|
||||
if (auto profile = GetProfileByName(name))
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto index = newTerminalArgs.ProfileIndex())
|
||||
{
|
||||
|
@ -541,6 +547,14 @@ Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs&
|
|||
return profile;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto commandLine = newTerminalArgs.Commandline(); !commandLine.empty())
|
||||
{
|
||||
if (auto profile = _getProfileForCommandLine(commandLine))
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
|
||||
|
@ -563,6 +577,179 @@ Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs&
|
|||
}
|
||||
}
|
||||
|
||||
// The method does some crude command line matching for our console hand-off support.
|
||||
// If you have hand-off enabled and start PowerShell from the start menu we might be called with
|
||||
// "C:\Program Files\PowerShell\7\pwsh.exe -WorkingDirectory ~"
|
||||
// This function then checks all known user profiles for one that's compatible with the commandLine.
|
||||
// In this case we might have a profile with the command line
|
||||
// "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
// This function will then match this profile return it.
|
||||
//
|
||||
// If no matching profile could be found a nullptr will be returned.
|
||||
Model::Profile CascadiaSettings::_getProfileForCommandLine(const winrt::hstring& commandLine) const
|
||||
{
|
||||
// We're going to cache all the command lines we got, as
|
||||
// _normalizeCommandLine is a relatively heavy operation.
|
||||
std::call_once(_commandLinesCacheOnce, [this]() {
|
||||
_commandLinesCache.reserve(_allProfiles.Size());
|
||||
|
||||
for (const auto& profile : _allProfiles)
|
||||
{
|
||||
if (const auto cmd = profile.Commandline(); !cmd.empty())
|
||||
{
|
||||
_commandLinesCache.emplace_back(_normalizeCommandLine(cmd.c_str()), profile);
|
||||
}
|
||||
}
|
||||
|
||||
// We're trying to find the command line with the longest common prefix below.
|
||||
// Given the commandLine "foo.exe -bar -baz" and these two user profiles:
|
||||
// * "foo.exe"
|
||||
// * "foo.exe -bar"
|
||||
// we want to choose the second one. By sorting the _commandLinesCache in a descending order
|
||||
// by command line length, we can return from this function the moment we found a matching
|
||||
// profile as there cannot possibly be any other profile anymore with a longer command line.
|
||||
std::stable_sort(_commandLinesCache.begin(), _commandLinesCache.end(), [](const auto& lhs, const auto& rhs) {
|
||||
return lhs.first.size() > rhs.first.size();
|
||||
});
|
||||
});
|
||||
|
||||
const auto needle = _normalizeCommandLine(commandLine.c_str());
|
||||
|
||||
// til::starts_with(string, prefix) will always return false if prefix.size() > string.size().
|
||||
// --> Using binary search we can safely skip all items in _commandLinesCache where .first.size() > needle.size().
|
||||
const auto end = _commandLinesCache.end();
|
||||
auto it = std::lower_bound(_commandLinesCache.begin(), end, needle, [&](const auto& lhs, const auto& rhs) {
|
||||
return lhs.first.size() > rhs.size();
|
||||
});
|
||||
|
||||
// `it` is now at a position where it->first.size() <= needle.size().
|
||||
// Hopefully we'll now find a command line with matching prefix.
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (til::starts_with(needle, it->first))
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Given a commandLine like the following:
|
||||
// * "C:\WINDOWS\System32\cmd.exe"
|
||||
// * "pwsh -WorkingDirectory ~"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe -WorkingDirectory ~"
|
||||
//
|
||||
// This function returns:
|
||||
// * "C:\Windows\System32\cmd.exe"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe\0-WorkingDirectory\0~"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe\0-WorkingDirectory\0~"
|
||||
//
|
||||
// The resulting strings are then used for comparisons in _getProfileForCommandLine().
|
||||
// For instance a resulting string of
|
||||
// "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
// is considered a compatible profile with
|
||||
// "C:\Program Files\PowerShell\7\pwsh.exe -WorkingDirectory ~"
|
||||
// as it shares the same (normalized) prefix.
|
||||
std::wstring CascadiaSettings::_normalizeCommandLine(LPCWSTR commandLine)
|
||||
{
|
||||
// Turn "%SystemRoot%\System32\cmd.exe" into "C:\WINDOWS\System32\cmd.exe".
|
||||
// We do this early, as environment variables might occur anywhere in the commandLine.
|
||||
std::wstring normalized;
|
||||
THROW_IF_FAILED(wil::ExpandEnvironmentStringsW(commandLine, normalized));
|
||||
|
||||
// One of the most important things this function does is to strip quotes.
|
||||
// That way the commandLine "foo.exe -bar" and "\"foo.exe\" \"-bar\"" appear identical.
|
||||
// We'll abuse CommandLineToArgvW for that as it's close to what CreateProcessW uses.
|
||||
int argc = 0;
|
||||
const auto argv = CommandLineToArgvW(normalized.c_str(), &argc);
|
||||
THROW_LAST_ERROR_IF(!argc);
|
||||
const auto argvRelease = wil::scope_exit([=]() { LocalFree(argv); });
|
||||
|
||||
// The given commandLine should start with an executable name or path.
|
||||
// For instance given the following argv arrays:
|
||||
// * {"C:\WINDOWS\System32\cmd.exe"}
|
||||
// * {"pwsh", "-WorkingDirectory", "~"}
|
||||
// * {"C:\Program", "Files\PowerShell\7\pwsh.exe"}
|
||||
// ^^^^
|
||||
// Notice how there used to be a space in the path, which was split by ExpandEnvironmentStringsW().
|
||||
// CreateProcessW() supports such atrocities, so we got to do the same.
|
||||
// * {"C:\Program Files\PowerShell\7\pwsh.exe", "-WorkingDirectory", "~"}
|
||||
//
|
||||
// This loop tries to resolve relative paths, as well as executable names in %PATH%
|
||||
// into absolute paths and normalizes them. The results for the above would be:
|
||||
// * "C:\Windows\System32\cmd.exe"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
// * "C:\Program Files\PowerShell\7\pwsh.exe"
|
||||
for (;;)
|
||||
{
|
||||
// CreateProcessW uses RtlGetExePath to get the lpPath for SearchPathW.
|
||||
// The difference between the behavior of SearchPathW if lpPath is nullptr and what RtlGetExePath returns
|
||||
// seems to be mostly whether SafeProcessSearchMode is respected and the support for relative paths.
|
||||
// Windows Terminal makes the use relative paths rather impractical which is why we simply dropped the call to RtlGetExePath.
|
||||
const auto status = wil::SearchPathW(nullptr, argv[0], L".exe", normalized);
|
||||
|
||||
if (status == S_OK)
|
||||
{
|
||||
std::filesystem::path path{ std::move(normalized) };
|
||||
|
||||
// ExpandEnvironmentStringsW() might have returned a string that's not in the canonical capitalization.
|
||||
// For instance %SystemRoot% is set to C:\WINDOWS on my system (ugh), even though the path is actually C:\Windows.
|
||||
// We need to fix this as case-sensitive path comparisons will fail otherwise (Windows supports case-sensitive file systems).
|
||||
// If we fail to resolve the path for whatever reason (pretty unlikely given that SearchPathW found it)
|
||||
// we fall back to leaving the path as is. Better than throwing a random exception and making this unusable.
|
||||
{
|
||||
std::error_code ec;
|
||||
auto canonicalPath = canonical(path, ec);
|
||||
if (!ec)
|
||||
{
|
||||
path = std::move(canonicalPath);
|
||||
}
|
||||
}
|
||||
|
||||
// std::filesystem::path has no way to extract the internal path.
|
||||
// So about that.... I own you, computer. Give me that path.
|
||||
normalized = std::move(const_cast<std::wstring&>(path.native()));
|
||||
break;
|
||||
}
|
||||
|
||||
// If the file path couldn't be found by SearchPathW this could be the result of us being given a commandLine
|
||||
// like "C:\foo bar\baz.exe -arg" which is resolved to the argv array {"C:\foo", "bar\baz.exe", "-arg"}.
|
||||
// Just like CreateProcessW() we thus try to concatenate arguments until we successfully resolve a valid path.
|
||||
// Of course we can only do that if we have at least 2 remaining arguments in argv.
|
||||
// All other error types aren't handled at the moment.
|
||||
if (argc < 2 || status != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// As described in the comment right above, we concatenate arguments in an attempt to resolve a valid path.
|
||||
// The code below turns argv from {"C:\foo", "bar\baz.exe", "-arg"} into {"C:\foo bar\baz.exe", "-arg"}.
|
||||
// The code abuses the fact that CommandLineToArgvW allocates all arguments back-to-back on the heap separated by '\0'.
|
||||
argv[1][-1] = L' ';
|
||||
--argc;
|
||||
}
|
||||
|
||||
// We've (hopefully) finished resolving the path to the executable.
|
||||
// We're now going to append all remaining arguments to the resulting string.
|
||||
// If argv is {"C:\Program Files\PowerShell\7\pwsh.exe", "-WorkingDirectory", "~"},
|
||||
// then we'll get "C:\Program Files\PowerShell\7\pwsh.exe\0-WorkingDirectory\0~"
|
||||
if (argc > 1)
|
||||
{
|
||||
// normalized contains a canonical form of argv[0] at this point.
|
||||
// -1 allows us to include the \0 between argv[0] and argv[1] in the call to append().
|
||||
const auto beg = argv[1] - 1;
|
||||
const auto lastArg = argv[argc - 1];
|
||||
const auto end = lastArg + wcslen(lastArg);
|
||||
normalized.append(beg, end);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper to get a profile given a name that could be a guid or an actual name.
|
||||
// Arguments:
|
||||
|
|
|
@ -140,8 +140,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
private:
|
||||
static const std::filesystem::path& _settingsPath();
|
||||
static std::wstring _normalizeCommandLine(LPCWSTR commandLine);
|
||||
|
||||
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
|
||||
Model::Profile _getProfileForCommandLine(const winrt::hstring& commandLine) const;
|
||||
|
||||
void _resolveDefaultProfile() const;
|
||||
|
||||
|
@ -165,6 +167,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
// defterm
|
||||
Model::DefaultTerminal _currentDefaultTerminal{ nullptr };
|
||||
|
||||
// GetProfileForArgs cache
|
||||
mutable std::once_flag _commandLinesCacheOnce;
|
||||
mutable std::vector<std::pair<std::wstring, Model::Profile>> _commandLinesCache;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -265,14 +265,12 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey,
|
|||
|
||||
std::wstring buffer;
|
||||
auto result = wil::AdaptFixedSizeToAllocatedResult<std::wstring, 256>(buffer, [&](PWSTR value, size_t valueLength, size_t* valueLengthNeededWithNull) -> HRESULT {
|
||||
auto length = static_cast<DWORD>(valueLength);
|
||||
auto length = gsl::narrow<DWORD>(valueLength * sizeof(wchar_t));
|
||||
const auto status = RegQueryValueExW(distroKey.get(), RegKeyDistroName, 0, nullptr, reinterpret_cast<BYTE*>(value), &length);
|
||||
// length will receive the number of bytes - convert to a number of
|
||||
// wchar_t's. AdaptFixedSizeToAllocatedResult will resize buffer to
|
||||
// valueLengthNeededWithNull
|
||||
*valueLengthNeededWithNull = (length / sizeof(wchar_t));
|
||||
// If you add one for another trailing null, then there'll actually
|
||||
// be _two_ trailing nulls in the buffer.
|
||||
// length will receive the number of bytes including trailing null byte. Convert to a number of wchar_t's.
|
||||
// AdaptFixedSizeToAllocatedResult will then resize buffer to valueLengthNeededWithNull.
|
||||
// We're rounding up to prevent infinite loops if the data isn't a REG_SZ and length isn't divisible by 2.
|
||||
*valueLengthNeededWithNull = (length + sizeof(wchar_t) - 1) / sizeof(wchar_t);
|
||||
return status == ERROR_MORE_DATA ? S_OK : HRESULT_FROM_WIN32(status);
|
||||
});
|
||||
|
||||
|
|
|
@ -453,7 +453,7 @@ try
|
|||
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(outPipeTheirSide.addressof(), outPipeOurSide.addressof(), nullptr, 0));
|
||||
|
||||
wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, TRUE, static_cast<DWORD>(connectMessage->Descriptor.Process)) };
|
||||
wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, TRUE, static_cast<DWORD>(connectMessage->Descriptor.Process)) };
|
||||
RETURN_LAST_ERROR_IF_NULL(clientProcess.get());
|
||||
|
||||
wil::unique_handle refHandle;
|
||||
|
|
Loading…
Reference in a new issue