I maintain CapyBro, a small open-source Windows app (.NET 8 / WPF) that rewrites or translates selected text with AI on a global hotkey. This week it picked up two distribution upgrades — and one of them surfaced a bug that's completely invisible until you package the app. Sharing both in case you ship desktop .NET.
1. It's on winget now
winget install RomanTykhonenko.CapyBro
The PR to microsoft/winget-pkgs got merged after the usual automated validation plus a human moderator pass (New-Package submissions always get a person to look). A few notes that might save you time:
- I generated the manifest with
wingetcreate— it handles the schema, the SHA256, and the device-flow auth that opens the PR for you. Far less error-prone than hand-writing the YAML. - My NSIS installer is unsigned, and it still passed validation cleanly, no warning labels. winget does not require code signing to get in. (SmartScreen on first run is a separate, end-user thing — not a winget gate.)
- Turnaround was a few days. The bot does the heavy lifting; you mostly wait on a moderator.
Updates later are a one-liner:
wingetcreate update RomanTykhonenko.CapyBro --version <X> --urls <url> --submit
2. The autostart bug that only exists when packaged
The Microsoft Store build is MSIX-packaged. The plain .exe uses the classic autostart trick: write HKCU\Software\Microsoft\Windows\CurrentVersion\Run. Works fine, shipped that way for ages.
Inside an MSIX package, that silently stops working. Writes to that Run key get virtualized into the package's private view, and Windows won't honor a virtualized Run entry for login startup. So the user flips "Start on login," the app dutifully writes the key, reports success... and never launches on the next login. No error, nothing in the logs.
The fix is to detect at runtime whether you're packaged, and switch backends:
-
Detection: P/Invoke
GetCurrentPackageFullName. It returnsAPPMODEL_ERROR_NO_PACKAGEwhen unpackaged, success when packaged. That one boolean picks the implementation. -
Packaged backend: the
Windows.ApplicationModel.StartupTaskWinRT API, plus awindows.startupTaskextension declared in the manifest (withuap10:Parametersto pass the same silent flag the.exeuses). Now the OS owns the startup entry — the user can even toggle it from Settings > Startup apps. - Unpackaged backend: the original Run-key writer, untouched.
Wiring it behind an IStartupTaskBackend chosen by a runtime check meant the .exe kept its exact old behavior and only the Store build got the new path — zero risk of regressing the thing that already worked.
A gotcha inside the gotcha: pulling in the WinRT projection (Windows.ApplicationModel.*) bumps your TFM to something like net8.0-windows10.0.17763.0 and drags in a chunky SDK projection. I didn't want the lean .exe carrying that, so the project multi-targets: the plain Win32 build excludes the WinRT files via #if, and only the packaged target compiles them in.
3. Native ARM64
While repackaging I added a native ARM64 build (win-arm64, ReadyToRun, cross-compiled on an x64 host) as a second package in the same Store submission. WPF runs natively on ARM64 .NET 8, so Surface / Snapdragon users skip the x64 emulation layer now. Honest caveat: I don't own ARM hardware, so it shipped smoke-tested on the build side but not on a real device — if you're on Windows-on-ARM, I'd genuinely love a sanity check.
winget for the CLI crowd; a Store build that actually autostarts and runs native on ARM64. It's MIT-licensed if you want to poke at the code: https://github.com/phantasmat2018/capy-bro and https://capybro.app. Happy to answer anything about the winget or MSIX side in the comments.













