Files
PowerToys/doc/devdocs/modules/cmdpal/powertoys-extension-local-development.md
Boliang Zhang 8536d7b1cd Fix MSIX sparse package DACL contamination breaking File Explorer preview handlers (#47177)
## Summary

Moves the MSIX sparse package's `ExternalLocation` from the PowerToys
root install folder to the `WinUI3Apps\` subfolder, isolating DACL
contamination from preview handler DLLs.

## Problem

On Windows 23H2/24H2/25H2, MSIX sparse package registration adds
AppContainer SIDs (`S-1-15-2-*` / `S-1-15-3-*`) to the DACL of the
`ExternalLocation` folder. Since `ExternalLocation` pointed to the
PowerToys root install folder, this broke File Explorer preview handlers
— `prevhost.exe` (running at LOW integrity) could no longer load preview
handler DLLs (`.txt`, `.md`, `.pdf`, `.svg`, etc.).

## Fix

- **`CustomAction.cpp`**: Changed `ExternalLocation` from
`installFolderPath` → `installFolderPath + L"WinUI3Apps\\"`
- **`AppxManifest.xml`**: Removed unused PowerOCR `<Application>` entry;
stripped `WinUI3Apps\` prefix from `Executable` paths (now relative to
new ExternalLocation)
- **`CmdPal.Ext.PowerToys.csproj`**: Moved `OutputPath` to `WinUI3Apps\`
so the AOT-compiled extension EXE resolves correctly under the new
ExternalLocation
- **WiX installer files**: Updated source/install paths for KBM assets,
CmdPal satellite assemblies, and `CommandPalette.Extensions.winmd` that
moved with the CmdPal output
- **ESRP signing**: Updated CmdPal dll/exe paths to include
`WinUI3Apps\` prefix

## Validation

| Test | 25H2 | 23H2 |
|------|------|------|
| DACL isolation (no S-1-15-* on root) |  |  |
| Preview handlers (.txt, .md, .pdf, .svg) |  |  |
| Peek |  |  |
| Context menus (PowerRename, FileLocksmith, ImageResizer, New+) |  | 
|
| Upgrade path (old → new) |  |  |

## Files changed (12)

- `installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp` — core
fix
- `src/PackageIdentity/AppxManifest.xml` — manifest cleanup
-
`src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj`
— output path
-
`src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/PowerToysResourcesHelper.cs`
— icon path
- `installer/PowerToysSetupVNext/BaseApplications.wxs` — winmd source
path
- `installer/PowerToysSetupVNext/KeyboardManager.wxs` — KBM assets path
- `installer/PowerToysSetupVNext/Resources.wxs` — CmdPal satellite paths
- `installer/PowerToysSetupVNext/generateAllFileComponents.ps1` — scan
path
- `.pipelines/ESRPSigning_core.json` — signing paths
- `src/PackageIdentity/BuildSparsePackage.ps1` — dev script hint
- `src/PackageIdentity/readme.md` — docs
- `doc/devdocs/modules/cmdpal/powertoys-extension-local-development.md`
— docs

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 22:37:59 +08:00

2.8 KiB

Local PowerToys Extension Development

This guide is for iterating on src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj.

The extension is registered through the shared sparse package defined in src/PackageIdentity/AppxManifest.xml. That manifest declares Microsoft.CmdPal.Ext.PowerToys.exe relative to the sparse package's ExternalLocation (WinUI3Apps\ subfolder), so the sparse package and the extension must be built for the same platform and configuration, for example x64\Debug.

Local development loop

  1. Build src/PackageIdentity/PackageIdentity.vcxproj.

    This creates PowerToysSparse.msix in the repo output root for the selected platform and configuration, and prints the Add-AppxPackage command you should run next.

  2. Trust the development certificate before running Add-AppxPackage.

    The PackageIdentity build creates or reuses src/PackageIdentity/.user/PowerToysSparse.certificate.sample.cer.

    Import it into CurrentUser\TrustedPeople:

    $repoRoot = "C:/git/PowerToys"
    Import-Certificate -FilePath "$repoRoot/src/PackageIdentity/.user/PowerToysSparse.certificate.sample.cer" -CertStoreLocation Cert:\CurrentUser\TrustedPeople
    

    If Windows still reports a trust failure such as 0x800B0109, also import the same certificate into Cert:\CurrentUser\TrustedRoot.

  3. Run the Add-AppxPackage command printed by the PackageIdentity build.

    That registers Microsoft.PowerToys.SparseApp as a sparse package and points it at the matching output root through -ExternalLocation.

    The command will look like this:

    Add-AppxPackage -Path "<repo>\<Platform>\<Configuration>\PowerToysSparse.msix" -ExternalLocation "<repo>\<Platform>\<Configuration>\WinUI3Apps"
    
  4. Build src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj in the same platform and configuration.

    This project writes Microsoft.CmdPal.Ext.PowerToys.exe into the WinUI3Apps\ subfolder of the output root, such as x64\Debug\WinUI3Apps or ARM64\Debug\WinUI3Apps. That matches the Executable="Microsoft.CmdPal.Ext.PowerToys.exe" entry in src/PackageIdentity/AppxManifest.xml resolved relative to the sparse package's ExternalLocation.

  5. Restart Command Palette.

    Close any running CmdPal instance and launch it again so it reloads app extensions and picks up the rebuilt Microsoft.CmdPal.Ext.PowerToys binaries.

When to repeat each step

  • Rebuild and re-register PackageIdentity when the sparse package manifest changes, the signing certificate changes, or you switch to a different output root such as ARM64\Debug.
  • For normal code changes in Microsoft.CmdPal.Ext.PowerToys, rebuilding the extension project and restarting CmdPal is enough.