diff --git a/.gitignore b/.gitignore index cf35973..990fde9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,11 @@ Installer/Output/ *.iss.bak *.exe.sig +# Windows App Runtime redistributable — exceeds GitHub's 100 MB limit. +# Download before building the installer: +# .\Installer\Download-Runtime.ps1 +Installer/WindowsAppRuntimeInstall-x64.exe + # ─── MSIX / AppPackages ─────────────────────────────────────────────────────── AppPackages/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c32c64..476c3ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.0.9] - 2026-05-04 + +### Fixed +- **HVCI crash (STATUS_INVALID_IMAGE_HASH)**: PassKey v1.0.7 and v1.0.8 crashed silently on + every PC with Hypervisor-Protected Code Integrity (HVCI) enabled — the majority of modern + Windows 11 systems with TPM. Windows Code Integrity rejected the NuGet-bundled + `Microsoft.UI.Xaml.dll` with exception code `0xc000027b` because NuGet copies are not + registered in the Windows system catalog. No window ever appeared and no crash log was + written (the exception is a native SEH fault that bypasses .NET `catch`). + + The fix removes `WindowsAppSDKSelfContained` from the build (the publish folder no longer + contains `Microsoft.UI.Xaml.dll`), adds an explicit `Bootstrap.Initialize(1.8)` call in + `Program.cs`, and bundles the official **Windows App Runtime 1.8** installer + (`WindowsAppRuntimeInstall-x64.exe`) inside the PassKey installer. The runtime is installed + silently during setup; its DLLs are fully trusted by Windows Code Integrity. + + A user-visible error dialog (Win32 `MessageBoxW`) is shown on the rare occasion that + `Bootstrap.Initialize` fails (e.g. corrupt runtime installation), replacing the previous + silent disappearance. + ## [1.0.8] - 2026-05-04 ### Fixed @@ -128,7 +148,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Giuseppe Imperato** — concept, design, product decisions - **[Claude](https://www.anthropic.com/claude) by Anthropic** — architecture, implementation, documentation -[Unreleased]: https://github.com/pexatar/PassKey/compare/v1.0.8...HEAD +[Unreleased]: https://github.com/pexatar/PassKey/compare/v1.0.9...HEAD +[1.0.9]: https://github.com/pexatar/PassKey/compare/v1.0.8...v1.0.9 [1.0.8]: https://github.com/pexatar/PassKey/compare/v1.0.7...v1.0.8 [1.0.7]: https://github.com/pexatar/PassKey/compare/v1.0.6...v1.0.7 [1.0.6]: https://github.com/pexatar/PassKey/compare/v1.0.5...v1.0.6 diff --git a/Installer/Download-Runtime.ps1 b/Installer/Download-Runtime.ps1 new file mode 100644 index 0000000..1c229b2 --- /dev/null +++ b/Installer/Download-Runtime.ps1 @@ -0,0 +1,19 @@ +# Download-Runtime.ps1 +# Downloads the Windows App Runtime 1.8.260101001 redistributable required to +# build the PassKey installer. The file is excluded from git (> 100 MB limit). +# +# Usage: .\Installer\Download-Runtime.ps1 +# Run once before building the installer with Inno Setup. + +$url = "https://aka.ms/windowsappsdk/1.8/1.8.260101001/windowsappruntimeinstall-x64.exe" +$output = Join-Path $PSScriptRoot "WindowsAppRuntimeInstall-x64.exe" + +if (Test-Path $output) { + Write-Host "Already present: $output" -ForegroundColor Green + exit 0 +} + +Write-Host "Downloading Windows App Runtime 1.8.260101001..." -ForegroundColor Cyan +Invoke-WebRequest -Uri $url -OutFile $output -UseBasicParsing +$sizeMB = [math]::Round((Get-Item $output).Length / 1MB, 1) +Write-Host "Done — $sizeMB MB saved to: $output" -ForegroundColor Green diff --git a/Installer/PassKey.iss b/Installer/PassKey.iss index 6663869..26460f6 100644 --- a/Installer/PassKey.iss +++ b/Installer/PassKey.iss @@ -4,8 +4,8 @@ [Setup] AppId={{A7F3C2D1-8E4B-4F9A-B6D5-3C1E7A2F0D84} AppName=PassKey -AppVersion=1.0.8 -AppVerName=PassKey 1.0.8 +AppVersion=1.0.9 +AppVerName=PassKey 1.0.9 AppPublisher=Giuseppe Imperato AppPublisherURL=https://github.com/pexatar/PassKey AppSupportURL=https://github.com/pexatar/PassKey/issues @@ -38,6 +38,12 @@ Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; Flags: unchecked [Files] +; Windows App Runtime 1.8 redistributable — installed silently before the app launches. +; HVCI (Hypervisor-Protected Code Integrity) rejects DLLs that are not in the Windows +; Code Integrity system catalog. The official runtime installer registers trusted, cataloged +; DLLs; the NuGet-bundled copies (WindowsAppSDKSelfContained) do not. +Source: "WindowsAppRuntimeInstall-x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall + ; Published self-contained output (Desktop + BrowserHost) Source: "..\publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs @@ -59,4 +65,11 @@ Root: HKCU; Subkey: "SOFTWARE\Google\Chrome\NativeMessagingHosts\com.passkey.hos Root: HKCU; Subkey: "SOFTWARE\Mozilla\NativeMessagingHosts\com.passkey.host"; ValueType: string; ValueData: "{app}\com.passkey.host.json"; Flags: uninsdeletevalue [Run] +; 1. Install Windows App Runtime 1.8 silently (waits for completion before continuing). +; RunOnceId prevents re-running on reinstall/repair if the runtime is already present. +Filename: "{tmp}\WindowsAppRuntimeInstall-x64.exe"; Parameters: "--quiet"; \ + StatusMsg: "Installing Windows App Runtime 1.8..."; \ + Flags: waituntilterminated; RunOnceId: "WinAppRuntime18" + +; 2. Launch PassKey after installation completes. Filename: "{app}\PassKey.Desktop.exe"; Description: "Launch PassKey"; Flags: nowait postinstall skipifsilent diff --git a/README.md b/README.md index 784dcc4..e3ed0fd 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,8 @@ PassKey stores your passwords, credit cards, identities, and secure notes in a l | Version | Platform | Type | |---------|----------|------| -| [v1.0.8](https://github.com/pexatar/PassKey/releases/tag/v1.0.8) | Windows x64 | Installer EXE | -| [v1.0.8](https://github.com/pexatar/PassKey/releases/tag/v1.0.8) | Windows x64 | Portable ZIP | +| [v1.0.9](https://github.com/pexatar/PassKey/releases/tag/v1.0.9) | Windows x64 | Installer EXE | +| [v1.0.9](https://github.com/pexatar/PassKey/releases/tag/v1.0.9) | Windows x64 | Portable ZIP | > **Requirements:** Windows 10 version 1809 (build 17763) or later, x64 processor. > No .NET runtime required — PassKey is fully self-contained. diff --git a/src/PassKey.Desktop/PassKey.Desktop.csproj b/src/PassKey.Desktop/PassKey.Desktop.csproj index 0ed73e7..fe907cb 100644 --- a/src/PassKey.Desktop/PassKey.Desktop.csproj +++ b/src/PassKey.Desktop/PassKey.Desktop.csproj @@ -12,12 +12,11 @@ true true None - true false PassKey.Desktop - 1.0.8 - 1.0.8.0 - 1.0.8.0 + 1.0.9 + 1.0.9.0 + 1.0.9.0 app.manifest Assets\PassKey.ico DISABLE_XAML_GENERATED_MAIN diff --git a/src/PassKey.Desktop/Program.cs b/src/PassKey.Desktop/Program.cs index 8882d9f..82b575b 100644 --- a/src/PassKey.Desktop/Program.cs +++ b/src/PassKey.Desktop/Program.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.InteropServices; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; @@ -61,6 +62,31 @@ public static void Main(string[] args) // ── App startup ────────────────────────────────────────────────────── // Note: ApplySavedLanguage() is called inside App() constructor before // InitializeComponent(), following the official Microsoft docs pattern. + + // ── Windows App Runtime bootstrap ──────────────────────────────────── + // Required for unpackaged apps with a custom Main (DISABLE_XAML_GENERATED_MAIN) + // when WindowsAppSDKSelfContained is NOT set. Locates and activates the + // system-installed Windows App Runtime 1.8 instead of NuGet-bundled copies. + // NuGet-bundled Microsoft.UI.Xaml.dll triggers STATUS_INVALID_IMAGE_HASH + // (0xc000027b) on HVCI-enabled systems because it is not in the Windows + // Code Integrity system catalog. + try + { + // 0x00010008 = (1 << 16) | 8 → minimum version 1.8 + Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap.Initialize(0x00010008); + } + catch (Exception ex) + { + WriteCrashLog(ex); + _ = MessageBoxW( + IntPtr.Zero, + "Windows App Runtime 1.8 is required but could not be initialised.\n\n" + + "Please reinstall PassKey to restore the required runtime.", + "PassKey — Missing Runtime", + 0x10 /* MB_ICONERROR */); + return; + } + try { WinRT.ComWrappersSupport.InitializeComWrappers(); @@ -118,4 +144,7 @@ private static void WriteCrashLog(Exception ex) // If we can't write the log, there is nothing more we can do. } } + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + private static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type); }