Files
PowerToys/installer/PowerToysSetupVNext/CliShims.wxs
moooyo ae416c045a 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
2026-08-14 14:32:27 +08:00

86 lines
5.1 KiB
XML

<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>