Skip to content

Commit d28ae33

Browse files
committed
fix ap package
1 parent 401ddcd commit d28ae33

12 files changed

Lines changed: 283 additions & 104 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"Version": "1.0.0",
3+
"UseMinimatch": false,
4+
"SignBatches": [
5+
{
6+
"MatchedPath": [
7+
"*.dll",
8+
"*.exe"
9+
],
10+
"SigningInfo": {
11+
"Operations": [
12+
{
13+
"KeyCode": "CP-230012",
14+
"OperationSetCode": "SigntoolSign",
15+
"Parameters": [
16+
{
17+
"parameterName": "OpusName",
18+
"parameterValue": "Microsoft"
19+
},
20+
{
21+
"parameterName": "OpusInfo",
22+
"parameterValue": "http://www.microsoft.com"
23+
},
24+
{
25+
"parameterName": "FileDigest",
26+
"parameterValue": "/fd \"SHA256\""
27+
},
28+
{
29+
"parameterName": "PageHash",
30+
"parameterValue": "/NPH"
31+
},
32+
{
33+
"parameterName": "TimeStamp",
34+
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
35+
}
36+
],
37+
"ToolName": "sign",
38+
"ToolVersion": "1.0"
39+
},
40+
{
41+
"KeyCode": "CP-230012",
42+
"OperationSetCode": "SigntoolVerify",
43+
"Parameters": [],
44+
"ToolName": "sign",
45+
"ToolVersion": "1.0"
46+
}
47+
]
48+
}
49+
}
50+
]
51+
}

.pipelines/v2/templates/job-build-project.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,30 @@ jobs:
481481
**\*UnitTest*.dll
482482
!**\obj\**
483483
484+
- pwsh: |-
485+
$Packages = Get-ChildItem -Recurse -Filter "PowerToys.AdvancedPaste_*.msix"
486+
Write-Host "Found $($Packages.Count) AdvancedPaste MSIX package(s):"
487+
foreach ($pkg in $Packages) {
488+
Write-Host " - $($pkg.FullName)"
489+
}
490+
491+
if ($Packages.Count -gt 0) {
492+
$PlatformPackage = $Packages | Where-Object { $_.Name -match "PowerToys\.AdvancedPaste_.*_(x64|arm64)\.msix$" } | Select-Object -First 1
493+
if ($PlatformPackage) {
494+
$Package = $PlatformPackage
495+
Write-Host "Using platform-specific package: $($Package.FullName)"
496+
} else {
497+
$Package = $Packages | Select-Object -First 1
498+
Write-Host "Using first available package: $($Package.FullName)"
499+
}
500+
501+
$PackageFilename = $Package.FullName
502+
Write-Host "##vso[task.setvariable variable=AdvancedPastePackagePath]${PackageFilename}"
503+
} else {
504+
Write-Warning "No AdvancedPaste MSIX packages found!"
505+
}
506+
displayName: Locate the AdvancedPaste MSIX
507+
484508
- pwsh: |-
485509
$Packages = Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix"
486510
Write-Host "Found $($Packages.Count) CmdPal MSIX package(s):"
@@ -530,6 +554,29 @@ jobs:
530554
Remove-Item -Force -Recurse "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore
531555
displayName: Re-pack the new CmdPal package after signing
532556
557+
- pwsh: |-
558+
& "$(MakeAppxPath)" unpack /p "$(AdvancedPastePackagePath)" /d "$(JobOutputDirectory)/AdvancedPastePackageContents"
559+
displayName: Unpack the AdvancedPaste MSIX for signing
560+
561+
- template: steps-esrp-signing.yml
562+
parameters:
563+
displayName: Sign AdvancedPaste MSIX content
564+
signingIdentity: ${{ parameters.signingIdentity }}
565+
inputs:
566+
FolderPath: '$(JobOutputDirectory)/AdvancedPastePackageContents'
567+
signType: batchSigning
568+
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_advancedpaste_msix_content.json'
569+
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
570+
571+
- pwsh: |-
572+
$outDir = New-Item -Type Directory "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore
573+
$PackageFilename = Join-Path $outDir.FullName (Split-Path -Leaf "$(AdvancedPastePackagePath)")
574+
& "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(JobOutputDirectory)/AdvancedPastePackageContents"
575+
Copy-Item -Force $PackageFilename "$(AdvancedPastePackagePath)"
576+
Remove-Item -Force -Recurse "$(JobOutputDirectory)/AdvancedPastePackageContents" -ErrorAction:Ignore
577+
Remove-Item -Force -Recurse "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore
578+
displayName: Re-pack the new AdvancedPaste package after signing
579+
533580
- pwsh: |
534581
$testsPath = "$(Build.SourcesDirectory)/$(BuildPlatform)/$(BuildConfiguration)/tests"
535582
if (Test-Path $testsPath) {
@@ -562,6 +609,10 @@ jobs:
562609
Copy-Item -Verbose -Force "$(CmdPalPackagePath)" "$(JobOutputDirectory)"
563610
displayName: Stage the final CmdPal package
564611
612+
- pwsh: |-
613+
Copy-Item -Verbose -Force "$(AdvancedPastePackagePath)" "$(JobOutputDirectory)"
614+
displayName: Stage the final AdvancedPaste package
615+
565616
566617
567618
- template: steps-build-installer-vnext.yml

doc/devdocs/modules/advancedpaste.md

Lines changed: 17 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ See the `ExecutePasteFormatAsync(PasteFormat, PasteActionSource)` method in `Opt
3333

3434
## Debugging
3535

36-
Advanced Paste outputs to its own subfolder (`WinUI3Apps/AdvancedPaste/`) rather than the shared `WinUI3Apps/` folder. This isolates its `resources.pri` from other WinUI 3 apps due to a [known WinUI bug](https://github.com/microsoft/microsoft-ui-xaml/issues/10856).
36+
Advanced Paste is packaged as a self-contained MSIX with its own identity (`Microsoft.PowerToys.AdvancedPaste`). This gives it native package identity for Windows AI APIs (Phi Silica) and clean `ms-appx:///` resource resolution without workarounds.
37+
38+
The MSIX is output to `WinUI3Apps/AdvancedPaste/` and registered by the module interface at runtime (or by the installer on release builds).
3739

3840
### Running and attaching the debugger
3941

@@ -44,78 +46,32 @@ Advanced Paste outputs to its own subfolder (`WinUI3Apps/AdvancedPaste/`) rather
4446

4547
Alternatively, use the VS Code launch configuration **"Run AdvancedPaste"** from [.vscode/launch.json](/.vscode/launch.json) to launch the exe directly — but note that without the Runner, IPC and hotkeys won't work.
4648

47-
### Sparse package identity (local development)
48-
49-
Advanced Paste uses the Windows AI APIs (Phi Silica / `Microsoft.Windows.AI.Text.LanguageModel`) which require **package identity** at runtime. PowerToys provides this via a shared sparse MSIX package (`Microsoft.PowerToys.SparseApp`).
50-
51-
#### Why is this needed?
52-
53-
- The `LanguageModel` API requires a Limited Access Feature (LAF) unlock, which only succeeds when the calling process has a matching package identity.
54-
- Advanced Paste is an unpackaged, self-contained WinUI 3 app. The sparse package grants it identity without converting it to a full MSIX.
55-
- There is a [known WinUI bug](https://github.com/microsoft/microsoft-ui-xaml/issues/10856) where self-contained WinUI 3 apps with sparse identity only load `resources.pri` instead of module-specific PRI files. To avoid conflicts with other WinUI apps, Advanced Paste outputs to its own subfolder (`WinUI3Apps/AdvancedPaste/`) and uses `resources.pri` as its PRI filename.
56-
57-
#### One-time setup
58-
59-
1. **Build the sparse package** for your platform and configuration:
60-
61-
```powershell
62-
pwsh src/PackageIdentity/BuildSparsePackage.ps1 -Platform ARM64 -Configuration Debug
63-
```
64-
65-
This generates `PowerToysSparse.msix` in `ARM64\Debug\`, creates a dev certificate, and signs the package.
66-
67-
2. **Trust the dev certificate** (first time only):
49+
### MSIX package identity
6850

69-
```powershell
70-
Import-Certificate -FilePath "src/PackageIdentity/.user/PowerToysSparse.certificate.sample.cer" -CertStoreLocation Cert:\CurrentUser\TrustedPeople
71-
```
51+
Advanced Paste uses the Windows AI APIs (Phi Silica / `Microsoft.Windows.AI.Text.LanguageModel`) which require **package identity** at runtime. The app is packaged as a self-contained MSIX (following the same pattern as Command Palette).
7252

73-
3. **Register the sparse package for development** by adding `-DevRegister` to the build command:
53+
#### How it works
7454

75-
```powershell
76-
pwsh src/PackageIdentity/BuildSparsePackage.ps1 -Platform ARM64 -Configuration Debug -DevRegister
77-
```
55+
- **Build**: The csproj has `EnableMsixTooling=true` and `GenerateAppxPackageOnBuild=true` (CI builds). This produces an MSIX in `AppPackages/`.
56+
- **Local dev**: VS registers the package automatically via `Add-AppxPackage -Register AppxManifest.xml` when you build.
57+
- **Installer**: The WiX installer deploys the MSIX file and a custom action (`InstallAdvancedPastePackageCA`) registers it via `PackageManager`.
7858

79-
The `-DevRegister` flag automatically:
80-
- Removes any existing registration
81-
- Creates a temporary copy of `AppxManifest.xml` with the dev publisher
82-
- Registers it via `Add-AppxPackage -Register` with `-ExternalLocation` pointing to your build output
83-
- Verifies the result
59+
#### Local development setup
8460

85-
You can combine it with the initial build (step 1) in a single command.
86-
87-
4. **Verify the registration:**
88-
89-
```powershell
90-
$pkg = Get-AppxPackage -Name "*SparseApp*"
91-
$pkg.Publisher # Should be: CN=PowerToys Dev, O=PowerToys, L=Redmond, S=Washington, C=US
92-
$pkg.PublisherId # Should be: djwsxzxb4ksa8
93-
$pkg.IsDevelopmentMode # Should be: True
94-
```
95-
96-
#### Re-registration
97-
98-
Re-register after rebuilding the sparse package, changing `AppxManifest.xml`, or switching platforms/configurations:
61+
For local debug builds, Visual Studio handles MSIX registration automatically. Select the **"PowerToys.AdvancedPaste (Package)"** launch profile in the debug dropdown.
9962

63+
To manually register:
10064
```powershell
101-
pwsh src/PackageIdentity/BuildSparsePackage.ps1 -Platform ARM64 -Configuration Debug -DevRegister
65+
Add-AppxPackage -Register "ARM64\Debug\WinUI3Apps\AdvancedPaste\AppxManifest.xml"
10266
```
10367

104-
#### Unregistering
105-
68+
Verify:
10669
```powershell
107-
pwsh src/PackageIdentity/BuildSparsePackage.ps1 -Unregister
70+
$pkg = Get-AppxPackage -Name "*AdvancedPaste*"
71+
$pkg.Name # Microsoft.PowerToys.AdvancedPaste.Dev
72+
$pkg.IsDevelopmentMode # True
10873
```
10974

110-
#### Troubleshooting
111-
112-
| Problem | Cause | Fix |
113-
|---------|-------|-----|
114-
| `Cannot locate resource from 'ms-appx:///Microsoft.UI.Xaml/Themes/themeresources.xaml'` | Sparse package not registered, or registered without `-ExternalLocation` | Re-register using step 3 above |
115-
| `IsDevelopmentMode` is `False` after registration | Used `Add-AppxPackage -Path *.msix` instead of `-Register` | Remove and re-register using the `-Register` form |
116-
| LAF unlock returns `Unavailable` | Publisher mismatch | Verify `$pkg.PublisherId` is `djwsxzxb4ksa8` |
117-
| `HRESULT 0x800B0109` (trust failure) | Dev certificate not trusted | Run `Import-Certificate` (step 2) for both `TrustedPeople` and `TrustedRoot` |
118-
11975
### How Settings UI checks Phi Silica availability
12076

12177
Settings UI does not have MSIX package identity. To check whether Phi Silica is available, it queries the running Advanced Paste process via a named pipe (`powertoys_advancedpaste_phi_status`).
@@ -125,10 +81,6 @@ Advanced Paste checks LAF + `GetReadyState()` once on startup (with MSIX identit
12581
- `NotReady` — model needs download via Windows Update
12682
- `NotSupported` — not a Copilot+ PC, API unavailable, or Advanced Paste not running
12783

128-
### See also
129-
130-
- [microsoft/microsoft-ui-xaml#10856](https://github.com/microsoft/microsoft-ui-xaml/issues/10856) — the WinUI bug requiring separate output folder
131-
13284
## Settings
13385

13486
| Setting | Description |

installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,80 @@ UINT __stdcall UnRegisterCmdPalPackageCA(MSIHANDLE hInstall)
14151415
return WcaFinalize(er);
14161416
}
14171417

1418+
UINT __stdcall InstallAdvancedPastePackageCA(MSIHANDLE hInstall)
1419+
{
1420+
using namespace winrt::Windows::Foundation;
1421+
using namespace winrt::Windows::Management::Deployment;
1422+
1423+
HRESULT hr = S_OK;
1424+
UINT er = ERROR_SUCCESS;
1425+
std::wstring installationFolder;
1426+
1427+
hr = WcaInitialize(hInstall, "InstallAdvancedPastePackage");
1428+
hr = getInstallFolder(hInstall, installationFolder);
1429+
1430+
try
1431+
{
1432+
auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\AdvancedPaste\\", false);
1433+
auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\AdvancedPaste\\Dependencies\\", true);
1434+
1435+
if (!msix.empty())
1436+
{
1437+
auto msixPath = msix[0];
1438+
1439+
if (!package::RegisterPackage(msixPath, dependencies))
1440+
{
1441+
Logger::error(L"Failed to install AdvancedPaste package");
1442+
er = ERROR_INSTALL_FAILURE;
1443+
}
1444+
}
1445+
}
1446+
catch (std::exception &e)
1447+
{
1448+
std::string errorMessage{"Exception thrown while trying to install AdvancedPaste package: "};
1449+
errorMessage += e.what();
1450+
Logger::error(errorMessage);
1451+
1452+
er = ERROR_INSTALL_FAILURE;
1453+
}
1454+
1455+
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1456+
return WcaFinalize(er);
1457+
}
1458+
1459+
UINT __stdcall UnRegisterAdvancedPastePackageCA(MSIHANDLE hInstall)
1460+
{
1461+
using namespace winrt::Windows::Foundation;
1462+
using namespace winrt::Windows::Management::Deployment;
1463+
1464+
HRESULT hr = S_OK;
1465+
UINT er = ERROR_SUCCESS;
1466+
1467+
hr = WcaInitialize(hInstall, "UnRegisterAdvancedPastePackageCA");
1468+
1469+
try
1470+
{
1471+
std::wstring packageToRemoveDisplayName{L"PowerToys Advanced Paste"};
1472+
1473+
if (!package::UnRegisterPackage(packageToRemoveDisplayName))
1474+
{
1475+
Logger::error(L"Failed to unregister package: " + packageToRemoveDisplayName);
1476+
er = ERROR_INSTALL_FAILURE;
1477+
}
1478+
}
1479+
catch (std::exception &e)
1480+
{
1481+
std::string errorMessage{"Exception thrown while trying to unregister the AdvancedPaste package: "};
1482+
errorMessage += e.what();
1483+
Logger::error(errorMessage);
1484+
1485+
er = ERROR_INSTALL_FAILURE;
1486+
}
1487+
1488+
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1489+
return WcaFinalize(er);
1490+
}
1491+
14181492

14191493
UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
14201494
{

installer/PowerToysSetupCustomActionsVNext/CustomAction.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ EXPORTS
2020
InstallEmbeddedMSIXCA
2121
InstallDSCModuleCA
2222
InstallCmdPalPackageCA
23+
InstallAdvancedPastePackageCA
2324
UnApplyModulesRegistryChangeSetsCA
2425
UnRegisterCmdPalPackageCA
26+
UnRegisterAdvancedPastePackageCA
2527
UnRegisterContextMenuPackagesCA
2628
UninstallEmbeddedMSIXCA
2729
UninstallDSCModuleCA

0 commit comments

Comments
 (0)