Implement basic profile matching (#11390)
This implements command line matching for `CascadiaSettings::GetProfileForArgs`. The command lines for all user profiles are resolved to absolute file paths, argument quotes are standardized ("canonicalized") and the results are cached. When `GetProfileForArgs` is called with a Commandline() value, we "canonicalize" the argument as well and find the profile that is the longest prefix. If none could be found the default profile is returned. ## PR Checklist * [x] Closes #9458 * [x] Closes #10952 * [x] I work here * [ ] Tests added/passed ## Validation Steps Performed * Open a `cmd.exe` tab in the store-version of WT * Run `start cmd` --> A tab with the `cmd.exe` profile opens * Run `start pwsh.exe` --> A tab with the PowerShell 7 profile opens * Run PowerShell 7 from the start menu --> A tab with the PowerShell 7 profile opens * Create a symlink for PowerShell 7 and launch `pwsh.exe` from there --> A tab with the PowerShell 7 profile opens
This commit is contained in:
parent
dd5dbb2a40
commit
479ef264b2
3
.github/actions/spelling/allow/microsoft.txt
vendored
3
.github/actions/spelling/allow/microsoft.txt
vendored
|
@ -1,5 +1,6 @@
|
||||||
ACLs
|
ACLs
|
||||||
ADMINS
|
ADMINS
|
||||||
|
advapi
|
||||||
altform
|
altform
|
||||||
altforms
|
altforms
|
||||||
appendwttlogging
|
appendwttlogging
|
||||||
|
@ -15,6 +16,7 @@ CPLs
|
||||||
cpptools
|
cpptools
|
||||||
cppvsdbg
|
cppvsdbg
|
||||||
CPRs
|
CPRs
|
||||||
|
cryptbase
|
||||||
DACL
|
DACL
|
||||||
DACLs
|
DACLs
|
||||||
diffs
|
diffs
|
||||||
|
@ -46,6 +48,7 @@ powershell
|
||||||
propkey
|
propkey
|
||||||
pscustomobject
|
pscustomobject
|
||||||
QWORD
|
QWORD
|
||||||
|
regedit
|
||||||
robocopy
|
robocopy
|
||||||
SACLs
|
SACLs
|
||||||
sdkddkver
|
sdkddkver
|
||||||
|
|
6
.github/actions/spelling/expect/expect.txt
vendored
6
.github/actions/spelling/expect/expect.txt
vendored
|
@ -668,6 +668,7 @@ dwriteglyphrundescriptionclustermap
|
||||||
dxgi
|
dxgi
|
||||||
dxgidwm
|
dxgidwm
|
||||||
dxinterop
|
dxinterop
|
||||||
|
dxsm
|
||||||
dxttbmp
|
dxttbmp
|
||||||
eachother
|
eachother
|
||||||
eae
|
eae
|
||||||
|
@ -1231,6 +1232,7 @@ KLF
|
||||||
KLMNO
|
KLMNO
|
||||||
KLMNOPQRST
|
KLMNOPQRST
|
||||||
KLMNOPQRSTQQQQQ
|
KLMNOPQRSTQQQQQ
|
||||||
|
KPRIORITY
|
||||||
KVM
|
KVM
|
||||||
langid
|
langid
|
||||||
LANGUAGELIST
|
LANGUAGELIST
|
||||||
|
@ -1641,6 +1643,7 @@ onecoreuapuuid
|
||||||
onecoreuuid
|
onecoreuuid
|
||||||
ONECOREWINDOWS
|
ONECOREWINDOWS
|
||||||
onehalf
|
onehalf
|
||||||
|
oneseq
|
||||||
ONLCR
|
ONLCR
|
||||||
openbash
|
openbash
|
||||||
opencode
|
opencode
|
||||||
|
@ -1707,6 +1710,7 @@ pcch
|
||||||
PCCHAR
|
PCCHAR
|
||||||
PCCONSOLE
|
PCCONSOLE
|
||||||
PCD
|
PCD
|
||||||
|
pcg
|
||||||
pch
|
pch
|
||||||
PCHAR
|
PCHAR
|
||||||
PCIDLIST
|
PCIDLIST
|
||||||
|
@ -1802,6 +1806,7 @@ POSX
|
||||||
POSXSCROLL
|
POSXSCROLL
|
||||||
POSYSCROLL
|
POSYSCROLL
|
||||||
ppci
|
ppci
|
||||||
|
PPEB
|
||||||
ppf
|
ppf
|
||||||
ppguid
|
ppguid
|
||||||
ppidl
|
ppidl
|
||||||
|
@ -2021,6 +2026,7 @@ Rike
|
||||||
RIPMSG
|
RIPMSG
|
||||||
RIS
|
RIS
|
||||||
RMENU
|
RMENU
|
||||||
|
rng
|
||||||
roadmap
|
roadmap
|
||||||
robomac
|
robomac
|
||||||
roundtrip
|
roundtrip
|
||||||
|
|
201
oss/pcg/LICENSE-APACHE.txt
Normal file
201
oss/pcg/LICENSE-APACHE.txt
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
19
oss/pcg/LICENSE-MIT.txt
Normal file
19
oss/pcg/LICENSE-MIT.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
14
oss/pcg/cgmanifest.json
Normal file
14
oss/pcg/cgmanifest.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"Registrations": [
|
||||||
|
{
|
||||||
|
"component": {
|
||||||
|
"type": "git",
|
||||||
|
"git": {
|
||||||
|
"repositoryUrl": "https://github.com/imneme/pcg-cpp",
|
||||||
|
"commitHash": "ffd522e7188bef30a00c74dc7eb9de5faff90092"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Version": 1
|
||||||
|
}
|
82
oss/pcg/include/pcg_random.hpp
Normal file
82
oss/pcg/include/pcg_random.hpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// PCG Random Number Generation for C++
|
||||||
|
//
|
||||||
|
// Copyright 2014-2019 Melissa O'Neill <oneill@pcg-random.org>,
|
||||||
|
// and the PCG Project contributors.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (provided in
|
||||||
|
// LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
// or under the MIT license (provided in LICENSE-MIT.txt and at
|
||||||
|
// http://opensource.org/licenses/MIT), at your option. This file may not
|
||||||
|
// be copied, modified, or distributed except according to those terms.
|
||||||
|
//
|
||||||
|
// Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either
|
||||||
|
// express or implied. See your chosen license for details.
|
||||||
|
//
|
||||||
|
// For additional information about the PCG random number generation scheme,
|
||||||
|
// visit http://www.pcg-random.org/.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Leonard Hecker <lhecker@microsoft.com>:
|
||||||
|
// The following contents are an extract of pcg_engines::oneseq_dxsm_64_32
|
||||||
|
// reduced down to the bare essentials, while retaining base functionality.
|
||||||
|
|
||||||
|
namespace pcg_engines {
|
||||||
|
class oneseq_dxsm_64_32 {
|
||||||
|
using xtype = uint32_t;
|
||||||
|
using itype = uint64_t;
|
||||||
|
|
||||||
|
itype state_;
|
||||||
|
|
||||||
|
static constexpr uint64_t multiplier() {
|
||||||
|
return 6364136223846793005ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint64_t increment() {
|
||||||
|
return 1442695040888963407ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static itype bump(itype state) {
|
||||||
|
return state * multiplier() + increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
itype base_generate0() {
|
||||||
|
itype old_state = state_;
|
||||||
|
state_ = bump(state_);
|
||||||
|
return old_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit oneseq_dxsm_64_32(itype state = 0xcafef00dd15ea5e5ULL) : state_(bump(state + increment())) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a value in the interval [0, UINT32_MAX].
|
||||||
|
xtype operator()() {
|
||||||
|
constexpr auto xtypebits = uint8_t(sizeof(xtype) * 8);
|
||||||
|
constexpr auto itypebits = uint8_t(sizeof(itype) * 8);
|
||||||
|
|
||||||
|
auto internal = base_generate0();
|
||||||
|
auto hi = xtype(internal >> (itypebits - xtypebits));
|
||||||
|
auto lo = xtype(internal);
|
||||||
|
|
||||||
|
lo |= 1;
|
||||||
|
hi ^= hi >> (xtypebits / 2);
|
||||||
|
hi *= xtype(multiplier());
|
||||||
|
hi ^= hi >> (3 * (xtypebits / 4));
|
||||||
|
hi *= lo;
|
||||||
|
return hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a value in the interval [0, upper_bound).
|
||||||
|
xtype operator()(xtype upper_bound) {
|
||||||
|
uint32_t threshold = (UINT64_MAX + uint32_t(1) - upper_bound) % upper_bound;
|
||||||
|
for (;;) {
|
||||||
|
auto r = operator()();
|
||||||
|
if (r >= threshold)
|
||||||
|
return r % upper_bound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <til/rand.h>
|
||||||
|
|
||||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||||
#include "../TerminalSettingsModel/TerminalSettings.h"
|
#include "../TerminalSettingsModel/TerminalSettings.h"
|
||||||
#include "TestUtils.h"
|
#include "TestUtils.h"
|
||||||
|
@ -34,14 +36,12 @@ namespace SettingsModelLocalTests
|
||||||
END_TEST_CLASS()
|
END_TEST_CLASS()
|
||||||
|
|
||||||
TEST_METHOD(TryCreateWinRTType);
|
TEST_METHOD(TryCreateWinRTType);
|
||||||
|
|
||||||
TEST_METHOD(TestTerminalArgsForBinding);
|
TEST_METHOD(TestTerminalArgsForBinding);
|
||||||
|
TEST_METHOD(CommandLineToArgvW);
|
||||||
|
TEST_METHOD(GetProfileForArgsWithCommandline);
|
||||||
TEST_METHOD(MakeSettingsForProfile);
|
TEST_METHOD(MakeSettingsForProfile);
|
||||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||||
|
|
||||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||||
|
|
||||||
TEST_METHOD(TestCommandlineToTitlePromotion);
|
TEST_METHOD(TestCommandlineToTitlePromotion);
|
||||||
|
|
||||||
TEST_CLASS_SETUP(ClassSetup)
|
TEST_CLASS_SETUP(ClassSetup)
|
||||||
|
@ -60,6 +60,139 @@ namespace SettingsModelLocalTests
|
||||||
VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize);
|
VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CascadiaSettings::_normalizeCommandLine abuses some aspects from CommandLineToArgvW
|
||||||
|
// to simplify the implementation. It assumes that all arguments returned by
|
||||||
|
// CommandLineToArgvW are returned back to back in memory as "arg1\0arg2\0arg3\0...".
|
||||||
|
// This test ensures CommandLineToArgvW doesn't change just to be sure.
|
||||||
|
void TerminalSettingsTests::CommandLineToArgvW()
|
||||||
|
{
|
||||||
|
pcg_engines::oneseq_dxsm_64_32 rng{ til::gen_random<uint64_t>() };
|
||||||
|
|
||||||
|
const auto expectedArgc = static_cast<int>(rng(16) + 1);
|
||||||
|
std::wstring expectedArgv;
|
||||||
|
std::wstring input;
|
||||||
|
|
||||||
|
// We generate up to 16 arguments. Each argument is up to 64 chars long, is quoted
|
||||||
|
// (2 chars, only applies to the input) and separated by a whitespace (1 char).
|
||||||
|
expectedArgv.reserve(expectedArgc * 65);
|
||||||
|
input.reserve(expectedArgc * 67);
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedArgc; ++i)
|
||||||
|
{
|
||||||
|
const bool useQuotes = static_cast<bool>(rng(2));
|
||||||
|
const auto count = static_cast<size_t>(rng(64));
|
||||||
|
const auto ch = static_cast<wchar_t>(rng('z' - 'a' + 1) + 'a');
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
expectedArgv.push_back(L'\0');
|
||||||
|
input.push_back(L' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useQuotes)
|
||||||
|
{
|
||||||
|
input.push_back(L'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedArgv.append(count, ch);
|
||||||
|
input.append(count, ch);
|
||||||
|
|
||||||
|
if (useQuotes)
|
||||||
|
{
|
||||||
|
input.push_back(L'"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int argc;
|
||||||
|
wil::unique_hlocal_ptr<PWSTR[]> argv{ ::CommandLineToArgvW(input.c_str(), &argc) };
|
||||||
|
VERIFY_ARE_EQUAL(expectedArgc, argc);
|
||||||
|
VERIFY_IS_NOT_NULL(argv);
|
||||||
|
|
||||||
|
const auto lastArg = argv[argc - 1];
|
||||||
|
const auto beg = argv[0];
|
||||||
|
const auto end = lastArg + wcslen(lastArg);
|
||||||
|
VERIFY_IS_GREATER_THAN(end, beg);
|
||||||
|
VERIFY_ARE_EQUAL(expectedArgv.size(), static_cast<size_t>(end - beg));
|
||||||
|
VERIFY_ARE_EQUAL(0, memcmp(beg, expectedArgv.data(), expectedArgv.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSettingsTests::GetProfileForArgsWithCommandline()
|
||||||
|
{
|
||||||
|
// I'm exclusively using cmd.exe as I know exactly where it resides at.
|
||||||
|
static constexpr std::string_view settingsJson{ R"({
|
||||||
|
"profiles": {
|
||||||
|
"defaults": {
|
||||||
|
"historySize": 123
|
||||||
|
},
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||||
|
"commandline": "%SystemRoot%\\System32\\cmd.exe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||||
|
"commandline": "cmd.exe /A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||||
|
"commandline": "cmd.exe /A /B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||||
|
"commandline": "cmd.exe /A /C",
|
||||||
|
"connectionType": "{9a9977a7-1fe0-49c0-b6c0-13a0cd1c98a1}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})" };
|
||||||
|
|
||||||
|
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
|
||||||
|
|
||||||
|
struct TestCase
|
||||||
|
{
|
||||||
|
std::wstring_view input;
|
||||||
|
int expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::array testCases{
|
||||||
|
// Base test.
|
||||||
|
TestCase{ L"cmd.exe", 0 },
|
||||||
|
// SearchPathW() normalization + case insensitive matching.
|
||||||
|
TestCase{ L"cmd.exe /a", 1 },
|
||||||
|
TestCase{ L"C:\\Windows\\System32\\cmd.exe /A", 1 },
|
||||||
|
// Test that we don't pick the equally long but different "/A /B" variant.
|
||||||
|
TestCase{ L"C:\\Windows\\System32\\cmd.exe /A /C", 1 },
|
||||||
|
// Test that we don't pick the shorter "/A" variant,
|
||||||
|
// but do pick the shorter "/A /B" variant for longer inputs.
|
||||||
|
TestCase{ L"cmd.exe /A /B", 2 },
|
||||||
|
TestCase{ L"cmd.exe /A /B /C", 2 },
|
||||||
|
// Ignore profiles with a connection type, like the Azure cloud shell.
|
||||||
|
// Instead it should pick any other prefix.
|
||||||
|
TestCase{ L"C:\\Windows\\System32\\cmd.exe /A /C", 1 },
|
||||||
|
// Return base layer profile for missing profiles.
|
||||||
|
TestCase{ L"C:\\Windows\\regedit.exe", -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& testCase : testCases)
|
||||||
|
{
|
||||||
|
NewTerminalArgs args;
|
||||||
|
args.Commandline(testCase.input);
|
||||||
|
|
||||||
|
const auto profile = settings->GetProfileForArgs(args);
|
||||||
|
VERIFY_IS_NOT_NULL(profile);
|
||||||
|
|
||||||
|
if (testCase.expected < 0)
|
||||||
|
{
|
||||||
|
VERIFY_ARE_EQUAL(123, profile.HistorySize());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUID expectedGUID{ 0x6239a42c, static_cast<uint16_t>(0x1111 * testCase.expected), 0x49a3, { 0x80, 0xbd, 0xe8, 0xfd, 0xd0, 0x45, 0x18, 0x5c } };
|
||||||
|
VERIFY_ARE_EQUAL(expectedGUID, static_cast<const GUID&>(profile.Guid()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TerminalSettingsTests::TestTerminalArgsForBinding()
|
void TerminalSettingsTests::TestTerminalArgsForBinding()
|
||||||
{
|
{
|
||||||
static constexpr std::string_view settingsJson{ R"(
|
static constexpr std::string_view settingsJson{ R"(
|
||||||
|
|
|
@ -218,7 +218,7 @@ namespace winrt::TerminalApp::implementation
|
||||||
_RegisterActionCallbacks();
|
_RegisterActionCallbacks();
|
||||||
|
|
||||||
// Hook up inbound connection event handler
|
// Hook up inbound connection event handler
|
||||||
TerminalConnection::ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
|
ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
|
||||||
|
|
||||||
//Event Bindings (Early)
|
//Event Bindings (Early)
|
||||||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||||
|
@ -2697,25 +2697,30 @@ namespace winrt::TerminalApp::implementation
|
||||||
return _isAlwaysOnTop;
|
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.
|
// 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.
|
// 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
|
// 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.
|
// and wait on it hence the locking mechanism.
|
||||||
if (Dispatcher().HasThreadAccess())
|
if (!Dispatcher().HasThreadAccess())
|
||||||
{
|
{
|
||||||
|
til::latch latch{ 1 };
|
||||||
|
HRESULT finalVal = S_OK;
|
||||||
|
|
||||||
|
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
|
||||||
|
finalVal = _OnNewConnection(connection);
|
||||||
|
latch.count_down();
|
||||||
|
});
|
||||||
|
|
||||||
|
latch.wait();
|
||||||
|
return finalVal;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NewTerminalArgs newTerminalArgs{};
|
NewTerminalArgs newTerminalArgs;
|
||||||
// TODO GH#10952: When we pass the actual commandline (or originating application), the
|
newTerminalArgs.Commandline(connection.Commandline());
|
||||||
// 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 profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||||
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||||
|
|
||||||
|
@ -2723,26 +2728,9 @@ namespace winrt::TerminalApp::implementation
|
||||||
|
|
||||||
// Request a summon of this window to the foreground
|
// Request a summon of this window to the foreground
|
||||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||||
}
|
|
||||||
CATCH_RETURN();
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
else
|
CATCH_RETURN()
|
||||||
{
|
|
||||||
til::latch latch{ 1 };
|
|
||||||
HRESULT finalVal = S_OK;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method Description:
|
// Method Description:
|
||||||
|
|
|
@ -388,7 +388,7 @@ namespace winrt::TerminalApp::implementation
|
||||||
winrt::Microsoft::Terminal::Settings::Model::Command _lastPreviewedCommand{ nullptr };
|
winrt::Microsoft::Terminal::Settings::Model::Command _lastPreviewedCommand{ nullptr };
|
||||||
std::vector<std::function<void()>> _restorePreviewFuncs{};
|
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 _HandleToggleInboundPty(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||||
|
|
||||||
void _WindowRenamerActionClick(const IInspectable& sender, const IInspectable& eventArgs);
|
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.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
#include "ConptyConnection.h"
|
#include "ConptyConnection.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <UserEnv.h>
|
||||||
#include <userenv.h>
|
#include <winternl.h>
|
||||||
|
|
||||||
#include "ConptyConnection.g.cpp"
|
#include "ConptyConnection.g.cpp"
|
||||||
#include "CTerminalHandoff.h"
|
#include "CTerminalHandoff.h"
|
||||||
|
@ -276,24 +276,18 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
const HANDLE hClientProcess) :
|
const HANDLE hClientProcess) :
|
||||||
_initialRows{ 25 },
|
_initialRows{ 25 },
|
||||||
_initialCols{ 80 },
|
_initialCols{ 80 },
|
||||||
_commandline{ L"" },
|
_guid{ Utils::CreateGuid() },
|
||||||
_startingDirectory{ L"" },
|
|
||||||
_startingTitle{ L"" },
|
|
||||||
_environment{ nullptr },
|
|
||||||
_guid{},
|
|
||||||
_u8State{},
|
|
||||||
_u16Str{},
|
|
||||||
_buffer{},
|
|
||||||
_inPipe{ hIn },
|
_inPipe{ hIn },
|
||||||
_outPipe{ hOut }
|
_outPipe{ hOut }
|
||||||
{
|
{
|
||||||
THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC));
|
THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC));
|
||||||
if (_guid == guid{})
|
|
||||||
{
|
|
||||||
_guid = Utils::CreateGuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
_piClient.hProcess = hClientProcess;
|
_piClient.hProcess = hClientProcess;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_commandline = _commandlineFromProcess(hClientProcess);
|
||||||
|
}
|
||||||
|
CATCH_LOG()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function Description:
|
// Function Description:
|
||||||
|
@ -355,6 +349,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
return _guid;
|
return _guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winrt::hstring ConptyConnection::Commandline() const
|
||||||
|
{
|
||||||
|
return _commandline;
|
||||||
|
}
|
||||||
|
|
||||||
void ConptyConnection::Start()
|
void ConptyConnection::Start()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -560,6 +559,38 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
}
|
}
|
||||||
CATCH_LOG()
|
CATCH_LOG()
|
||||||
|
|
||||||
|
// Returns the command line of the given process.
|
||||||
|
// Requires PROCESS_BASIC_INFORMATION | PROCESS_VM_READ privileges.
|
||||||
|
winrt::hstring ConptyConnection::_commandlineFromProcess(HANDLE process)
|
||||||
|
{
|
||||||
|
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()
|
DWORD ConptyConnection::_OutputThread()
|
||||||
{
|
{
|
||||||
// Keep us alive until the output thread terminates; the destructor
|
// Keep us alive until the output thread terminates; the destructor
|
||||||
|
@ -636,8 +667,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept
|
HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto conn = winrt::make<implementation::ConptyConnection>(signal, in, out, ref, server, client);
|
_newConnectionHandlers(winrt::make<ConptyConnection>(signal, in, out, ref, server, client));
|
||||||
_newConnectionHandlers(conn);
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
void ClearBuffer();
|
void ClearBuffer();
|
||||||
|
|
||||||
winrt::guid Guid() const noexcept;
|
winrt::guid Guid() const noexcept;
|
||||||
|
winrt::hstring Commandline() const;
|
||||||
|
|
||||||
static void StartInboundListener();
|
static void StartInboundListener();
|
||||||
static void StopInboundListener();
|
static void StopInboundListener();
|
||||||
|
@ -56,12 +57,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
|
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
|
||||||
|
|
||||||
private:
|
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;
|
HRESULT _LaunchAttachedClient() noexcept;
|
||||||
void _indicateExitWithStatus(unsigned int status) noexcept;
|
void _indicateExitWithStatus(unsigned int status) noexcept;
|
||||||
void _ClientTerminated() 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 _initialRows{};
|
||||||
uint32_t _initialCols{};
|
uint32_t _initialCols{};
|
||||||
hstring _commandline{};
|
hstring _commandline{};
|
||||||
|
|
|
@ -5,10 +5,13 @@ import "ITerminalConnection.idl";
|
||||||
|
|
||||||
namespace Microsoft.Terminal.TerminalConnection
|
namespace Microsoft.Terminal.TerminalConnection
|
||||||
{
|
{
|
||||||
|
delegate void NewConnectionHandler(ConptyConnection connection);
|
||||||
|
|
||||||
[default_interface] runtimeclass ConptyConnection : ITerminalConnection
|
[default_interface] runtimeclass ConptyConnection : ITerminalConnection
|
||||||
{
|
{
|
||||||
ConptyConnection();
|
ConptyConnection();
|
||||||
Guid Guid { get; };
|
Guid Guid { get; };
|
||||||
|
String Commandline { get; };
|
||||||
void ClearBuffer();
|
void ClearBuffer();
|
||||||
|
|
||||||
static event NewConnectionHandler NewConnection;
|
static event NewConnectionHandler NewConnection;
|
||||||
|
|
|
@ -29,6 +29,4 @@ namespace Microsoft.Terminal.TerminalConnection
|
||||||
event Windows.Foundation.TypedEventHandler<ITerminalConnection, Object> StateChanged;
|
event Windows.Foundation.TypedEventHandler<ITerminalConnection, Object> StateChanged;
|
||||||
ConnectionState State { get; };
|
ConnectionState State { get; };
|
||||||
};
|
};
|
||||||
|
|
||||||
delegate void NewConnectionHandler(ITerminalConnection connection);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#include <VersionHelpers.h>
|
#include <VersionHelpers.h>
|
||||||
#include <WtExeUtils.h>
|
#include <WtExeUtils.h>
|
||||||
|
|
||||||
|
#include <shellapi.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
|
||||||
using namespace winrt::Microsoft::Terminal;
|
using namespace winrt::Microsoft::Terminal;
|
||||||
using namespace winrt::Microsoft::Terminal::Settings;
|
using namespace winrt::Microsoft::Terminal::Settings;
|
||||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||||
|
@ -539,10 +542,13 @@ Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs&
|
||||||
{
|
{
|
||||||
if (newTerminalArgs)
|
if (newTerminalArgs)
|
||||||
{
|
{
|
||||||
if (auto profile = GetProfileByName(newTerminalArgs.Profile()))
|
if (const auto name = newTerminalArgs.Profile(); !name.empty())
|
||||||
|
{
|
||||||
|
if (auto profile = GetProfileByName(name))
|
||||||
{
|
{
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto index = newTerminalArgs.ProfileIndex())
|
if (const auto index = newTerminalArgs.ProfileIndex())
|
||||||
{
|
{
|
||||||
|
@ -551,6 +557,14 @@ Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs&
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto commandLine = newTerminalArgs.Commandline(); !commandLine.empty())
|
||||||
|
{
|
||||||
|
if (auto profile = _getProfileForCommandLine(commandLine))
|
||||||
|
{
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
|
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
|
||||||
|
@ -573,6 +587,199 @@ 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 (profile.ConnectionType() != winrt::guid{})
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cmd = profile.Commandline();
|
||||||
|
if (cmd.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_commandLinesCache.emplace_back(_normalizeCommandLine(cmd.c_str()), profile);
|
||||||
|
}
|
||||||
|
CATCH_LOG()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const auto& prefix = it->first;
|
||||||
|
const auto length = gsl::narrow<int>(prefix.size());
|
||||||
|
if (CompareStringOrdinal(needle.data(), length, prefix.data(), length, TRUE) == CSTR_EQUAL)
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG_CAUGHT_EXCEPTION();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
wil::unique_hlocal_ptr<PWSTR[]> argv{ CommandLineToArgvW(normalized.c_str(), &argc) };
|
||||||
|
THROW_LAST_ERROR_IF(!argc);
|
||||||
|
|
||||||
|
// 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 = std::filesystem::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:
|
// Method Description:
|
||||||
// - Helper to get a profile given a name that could be a guid or an actual name.
|
// - Helper to get a profile given a name that could be a guid or an actual name.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
|
|
@ -141,8 +141,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const std::filesystem::path& _settingsPath();
|
static const std::filesystem::path& _settingsPath();
|
||||||
|
static std::wstring _normalizeCommandLine(LPCWSTR commandLine);
|
||||||
|
|
||||||
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
|
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
|
||||||
|
Model::Profile _getProfileForCommandLine(const winrt::hstring& commandLine) const;
|
||||||
void _refreshDefaultTerminals();
|
void _refreshDefaultTerminals();
|
||||||
|
|
||||||
void _resolveDefaultProfile() const;
|
void _resolveDefaultProfile() const;
|
||||||
|
@ -168,6 +170,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||||
// defterm
|
// defterm
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> _defaultTerminals{ nullptr };
|
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> _defaultTerminals{ nullptr };
|
||||||
Model::DefaultTerminal _currentDefaultTerminal{ nullptr };
|
Model::DefaultTerminal _currentDefaultTerminal{ nullptr };
|
||||||
|
|
||||||
|
// GetProfileForArgs cache
|
||||||
|
mutable std::once_flag _commandLinesCacheOnce;
|
||||||
|
mutable std::vector<std::pair<std::wstring, Model::Profile>> _commandLinesCache;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -269,14 +269,12 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey,
|
||||||
|
|
||||||
std::wstring buffer;
|
std::wstring buffer;
|
||||||
auto result = wil::AdaptFixedSizeToAllocatedResult<std::wstring, 256>(buffer, [&](PWSTR value, size_t valueLength, size_t* valueLengthNeededWithNull) -> HRESULT {
|
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);
|
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
|
// length will receive the number of bytes including trailing null byte. Convert to a number of wchar_t's.
|
||||||
// wchar_t's. AdaptFixedSizeToAllocatedResult will resize buffer to
|
// AdaptFixedSizeToAllocatedResult will then resize buffer to valueLengthNeededWithNull.
|
||||||
// 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));
|
*valueLengthNeededWithNull = (length + sizeof(wchar_t) - 1) / sizeof(wchar_t);
|
||||||
// If you add one for another trailing null, then there'll actually
|
|
||||||
// be _two_ trailing nulls in the buffer.
|
|
||||||
return status == ERROR_MORE_DATA ? S_OK : HRESULT_FROM_WIN32(status);
|
return status == ERROR_MORE_DATA ? S_OK : HRESULT_FROM_WIN32(status);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;$(SolutionDir)\oss\boost\boost_1_73_0;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\dep\Win32K;$(SolutionDir)\oss\boost\boost_1_73_0;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\interval_tree;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\pcg\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<MinimalRebuild>false</MinimalRebuild>
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||||
|
|
|
@ -31,9 +31,7 @@
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the actual field name, but in the public SDK, it's named Reserved3. We need to pursue publishing the real name.
|
*ProcessId = (ULONG)BasicInfo.InheritedFromUniqueProcessId;
|
||||||
//*ProcessId = (ULONG)BasicInfo.InheritedFromUniqueProcessId;
|
|
||||||
*ProcessId = (ULONG)BasicInfo.Reserved3;
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,12 @@ typedef enum _PROCESSINFOCLASS
|
||||||
|
|
||||||
typedef struct _PROCESS_BASIC_INFORMATION
|
typedef struct _PROCESS_BASIC_INFORMATION
|
||||||
{
|
{
|
||||||
PVOID Reserved1;
|
NTSTATUS ExitStatus;
|
||||||
PVOID PebBaseAddress;
|
PVOID PebBaseAddress;
|
||||||
PVOID Reserved2[2];
|
ULONG_PTR AffinityMask;
|
||||||
|
LONG BasePriority;
|
||||||
ULONG_PTR UniqueProcessId;
|
ULONG_PTR UniqueProcessId;
|
||||||
ULONG_PTR Reserved3;
|
ULONG_PTR InheritedFromUniqueProcessId;
|
||||||
} PROCESS_BASIC_INFORMATION;
|
} PROCESS_BASIC_INFORMATION;
|
||||||
typedef PROCESS_BASIC_INFORMATION* PPROCESS_BASIC_INFORMATION;
|
typedef PROCESS_BASIC_INFORMATION* PPROCESS_BASIC_INFORMATION;
|
||||||
|
|
||||||
|
|
|
@ -453,7 +453,7 @@ try
|
||||||
|
|
||||||
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(outPipeTheirSide.addressof(), outPipeOurSide.addressof(), nullptr, 0));
|
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());
|
RETURN_LAST_ERROR_IF_NULL(clientProcess.get());
|
||||||
|
|
||||||
wil::unique_handle refHandle;
|
wil::unique_handle refHandle;
|
||||||
|
|
47
src/inc/til/rand.h
Normal file
47
src/inc/til/rand.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
#include <pcg_random.hpp>
|
||||||
|
|
||||||
|
namespace til
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
typedef BOOLEAN(APIENTRY* RtlGenRandom)(PVOID RandomBuffer, ULONG RandomBufferLength);
|
||||||
|
|
||||||
|
struct RtlGenRandomLoader
|
||||||
|
{
|
||||||
|
RtlGenRandomLoader() noexcept :
|
||||||
|
// The documentation states to use advapi32.dll, but technically
|
||||||
|
// SystemFunction036 lives in cryptbase.dll since Windows 7.
|
||||||
|
module{ LoadLibraryExW(L"cryptbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) },
|
||||||
|
proc{ reinterpret_cast<decltype(proc)>(GetProcAddress(module.get(), "SystemFunction036")) }
|
||||||
|
{
|
||||||
|
FAIL_FAST_LAST_ERROR_IF(!proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void operator()(PVOID RandomBuffer, ULONG RandomBufferLength)
|
||||||
|
{
|
||||||
|
proc(RandomBuffer, RandomBufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wil::unique_hmodule module;
|
||||||
|
RtlGenRandom proc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void gen_random(void* data, uint32_t length)
|
||||||
|
{
|
||||||
|
static details::RtlGenRandomLoader loader;
|
||||||
|
loader(data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_standard_layout_v<T>>>
|
||||||
|
T gen_random()
|
||||||
|
{
|
||||||
|
T value;
|
||||||
|
gen_random(&value, sizeof(T));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue