mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 10:09:43 +02:00
Add native CLI shims for PowerToys command-line tools (#48631)
## Summary of the Pull Request Adds a native C++ multi-call shim that exposes existing PowerToys CLIs through `PATH`. The shims are installed under the PowerToys `bin` subfolder and follow the `PowerToys.<ModuleName>.CLI.exe` naming convention. The launcher preserves the raw argument tail, shares the caller's console, and returns the target process exit code. | PATH-visible command | Target executable | | --- | --- | | `PowerToys.FancyZones.CLI.exe` | `FancyZonesCLI.exe` | | `PowerToys.ImageResizer.CLI.exe` | `WinUI3Apps/PowerToys.ImageResizerCLI.exe` | | `PowerToys.FileLocksmith.CLI.exe` | `FileLocksmithCLI.exe` | | `PowerToys.PowerDisplay.CLI.exe` | `WinUI3Apps/PowerToys.PowerDisplay.Cli.exe` | proof of this work: <img width="1044" height="294" alt="image" src="https://github.com/user-attachments/assets/b659c552-5c08-4430-85c3-eba48f286eb0" /> <img width="1137" height="244" alt="image" src="https://github.com/user-attachments/assets/5fed493f-dc30-428d-a618-bf612ccf3635" /> <img width="1727" height="868" alt="image" src="https://github.com/user-attachments/assets/5c32fd2e-4a3a-4138-b968-fb5434eebec3" /> ## PR Checklist - [x] Closes: #48634 - [x] **Communication:** Discussed with core contributors in this PR - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** CLI diagnostic messages are not localized - [x] **Dev docs:** Updated CLI naming and installation conventions - [x] **New binaries:** Added on the required places - [x] Signing JSON - [x] WiX installer entries - [x] CI builds through `PowerToys.slnx`; no dedicated YML step is required - [x] The existing release pipeline covers the solution and installer - [x] **Documentation updated:** `doc/devdocs/cli-conventions.md` ## Detailed Description of the Pull Request / Additional comments - Uses one native launcher binary for all commands and resolves the target from the invoked shim filename. - Installs PATH-visible shims under `PowerToys\bin`. - Keeps the existing module CLI binaries and their deployment locations unchanged. - Rejects the previous unsuffixed and `*cli` command aliases. ## Validation Steps Performed - Built `tools/CliShim.UnitTests/CliShim.UnitTests.vcxproj` in `Release|x64`: 0 warnings, 0 errors. - Ran `CliShim.UnitTests.dll` with `vstest.console.exe`: 5/5 tests passed. - Verified the CLI manifest, WiX command names, and `bin` installation directory are synchronized. - Ran `git diff --check`. --------- Co-authored-by: Yu Leng <yuleng@microsoft.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d11c4221-248f-44a9-85fb-7017ed43f4ce
This commit is contained in:
85
installer/PowerToysSetupVNext/CliShims.wxs
Normal file
85
installer/PowerToysSetupVNext/CliShims.wxs
Normal file
@@ -0,0 +1,85 @@
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
|
||||
<?include $(sys.CURRENTDIR)\Common.wxi?>
|
||||
|
||||
<!--
|
||||
PATH-visible shims forward to the installed PowerToys CLIs. Every command is the same
|
||||
PowerToys.CliShim.exe payload installed under a different name; the shim resolves its target
|
||||
from that name. tools\CliShim\CliShimManifest.props is the source of truth for the list and
|
||||
the CliShim project fails the build if the two drift apart, so keep the File/@Name values and
|
||||
that manifest in sync.
|
||||
-->
|
||||
<Fragment>
|
||||
<DirectoryRef Id="CliFolder">
|
||||
<Component Id="CliShim_PowerToys_FancyZones_CLI_exe" Guid="63800E2A-21B9-4A92-AD25-9C1DC7593F4F" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_PowerToys_FancyZones_CLI_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)CliShim\PowerToys.CliShim.exe" Name="PowerToys.FancyZones.CLI.exe" Id="CliShim_PowerToys.FancyZones.CLI.exe" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="CliShim_PowerToys_ImageResizer_CLI_exe" Guid="2BC274AA-FECD-4A85-BF70-06670A6B99A5" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_PowerToys_ImageResizer_CLI_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)CliShim\PowerToys.CliShim.exe" Name="PowerToys.ImageResizer.CLI.exe" Id="CliShim_PowerToys.ImageResizer.CLI.exe" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="CliShim_PowerToys_FileLocksmith_CLI_exe" Guid="2DD830DD-78D4-46EE-B23B-3EAB3D838EDF" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_PowerToys_FileLocksmith_CLI_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)CliShim\PowerToys.CliShim.exe" Name="PowerToys.FileLocksmith.CLI.exe" Id="CliShim_PowerToys.FileLocksmith.CLI.exe" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="CliShim_PowerToys_PowerDisplay_CLI_exe" Guid="E98A36C9-384E-48CB-A844-A6A49E4138D9" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_PowerToys_PowerDisplay_CLI_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)CliShim\PowerToys.CliShim.exe" Name="PowerToys.PowerDisplay.CLI.exe" Id="CliShim_PowerToys.PowerDisplay.CLI.exe" Checksum="yes" />
|
||||
</Component>
|
||||
|
||||
<!-- MSI broadcasts WM_SETTINGCHANGE; terminals open before install must be reopened. -->
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<Component Id="cli_env_path_user" Guid="87AA86E8-81E8-44CB-9519-FC70CF7BEA56" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="cli_env_path_user" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<Environment Id="AddCliToUserPath" Name="PATH" Action="set" Part="last" System="no" Value="[CliFolder]" />
|
||||
</Component>
|
||||
<?else?>
|
||||
<Component Id="cli_env_path_machine" Guid="739F3916-2247-4F1B-8BBD-0949A5B870F7" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="cli_env_path_machine" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<Environment Id="AddCliToMachinePath" Name="PATH" Action="set" Part="last" System="yes" Value="[CliFolder]" />
|
||||
<!--
|
||||
On the same component as the PATH entry on purpose: bin is only worth locking down
|
||||
because it is on the machine PATH, and this way the two cannot drift apart.
|
||||
CreateFolders applies the DACL before InstallFiles writes the shims, so the shims
|
||||
inherit it and need no PermissionEx of their own.
|
||||
-->
|
||||
<CreateFolder>
|
||||
<PermissionEx Sddl="$(var.MachinePathFolderSddl)" />
|
||||
</CreateFolder>
|
||||
</Component>
|
||||
<?endif?>
|
||||
</DirectoryRef>
|
||||
|
||||
<ComponentGroup Id="CliShimsComponentGroup">
|
||||
<Component Id="RemoveCliFolder" Guid="831C56D8-9FEB-41B2-8A8C-BD49487479BB" Directory="CliFolder">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveCliFolder" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderCliFolder" Directory="CliFolder" On="uninstall" />
|
||||
</Component>
|
||||
<ComponentRef Id="CliShim_PowerToys_FancyZones_CLI_exe" />
|
||||
<ComponentRef Id="CliShim_PowerToys_ImageResizer_CLI_exe" />
|
||||
<ComponentRef Id="CliShim_PowerToys_FileLocksmith_CLI_exe" />
|
||||
<ComponentRef Id="CliShim_PowerToys_PowerDisplay_CLI_exe" />
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<ComponentRef Id="cli_env_path_user" />
|
||||
<?else?>
|
||||
<ComponentRef Id="cli_env_path_machine" />
|
||||
<?endif?>
|
||||
</ComponentGroup>
|
||||
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -54,5 +54,21 @@
|
||||
<?define UpgradeCodeGUID="42B84BF7-5FBF-473B-9C8B-049DC16F7708" ?>
|
||||
<?endif?>
|
||||
|
||||
<!--
|
||||
DACL for every folder PowerToys adds to the machine PATH.
|
||||
|
||||
D:P is the point: it drops inherited ACEs. A per-machine INSTALLFOLDER is overridable
|
||||
(PowerToys.wxs InstallFolder), and a custom root such as C:\PowerToys inherits
|
||||
"Authenticated Users: Modify" from the drive root - which would let any standard user plant
|
||||
binaries in a folder that every process on the machine searches. SYSTEM and Administrators
|
||||
keep full control; BUILTIN\Users keep read + execute so non-admins can still run the commands.
|
||||
|
||||
Author this on the same component as the folder's Environment PATH entry, so a new
|
||||
PATH-visible folder cannot ship without it. Per-user installs live under %LOCALAPPDATA% and
|
||||
must NOT use this - InstallPrivileges is "limited" there, and this SDDL would leave the
|
||||
installing user with only read + execute on their own install.
|
||||
-->
|
||||
<?define MachinePathFolderSddl="D:PAI(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GRGX;;;BU)(A;OICIIO;GA;;;CO)" ?>
|
||||
|
||||
<?define BinX32Dir="$(var.RepoDir)x86\$(var.Configuration)\" ?>
|
||||
</Include>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<RegistryValue Type="string" Name="SecureDSCModulesFolder" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<CreateFolder>
|
||||
<PermissionEx Sddl="D:PAI(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GRGX;;;BU)(A;OICIIO;GA;;;CO)" />
|
||||
<PermissionEx Sddl="$(var.MachinePathFolderSddl)" />
|
||||
</CreateFolder>
|
||||
</Component>
|
||||
<?endif?>
|
||||
|
||||
@@ -136,6 +136,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
<Compile Include="Settings.wxs" />
|
||||
<Compile Include="ShortcutGuide.wxs" />
|
||||
<Compile Include="Tools.wxs" />
|
||||
<Compile Include="CliShims.wxs" />
|
||||
<Compile Include="MouseWithoutBorders.wxs" />
|
||||
<Compile Include="WinUI3Applications.wxs" />
|
||||
<Compile Include="MonacoSRC.wxs" />
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<ComponentGroupRef Id="DscResourcesComponentGroup" />
|
||||
<ComponentGroupRef Id="WindowsAppSDKComponentGroup" />
|
||||
<ComponentGroupRef Id="ToolComponentGroup" />
|
||||
<ComponentGroupRef Id="CliShimsComponentGroup" />
|
||||
<ComponentGroupRef Id="MonacoSRCHeatGenerated" />
|
||||
<ComponentGroupRef Id="WorkspacesComponentGroup" />
|
||||
<ComponentGroupRef Id="CmdPalComponentGroup" />
|
||||
@@ -299,6 +300,7 @@
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ToolsFolder" Name="Tools" />
|
||||
<Directory Id="CliFolder" Name="bin" />
|
||||
</Directory>
|
||||
</StandardDirectory>
|
||||
<StandardDirectory Id="ProgramMenuFolder">
|
||||
|
||||
Reference in New Issue
Block a user