Як я завів WPF-застосунок у winget і Microsoft Store — і баг автозапуску, що вилазить тільки в MSIX
Я підтримую CapyBro — невеликий open-source застосунок для Windows (.NET 8 / WPF), що переписує чи перекладає виділений текст за допомогою AI по глобальному хоткею. Цього тижня він отримав два апгрейди дистрибуції, і один із них витягнув на поверхню баг, якого взагалі не видно, поки не запакуєш застосунок. Ділюся обома — раптом ви теж пакуєте десктопний .NET.
1. Тепер є у winget
winget install RomanTykhonenko.CapyBro
PR у microsoft/winget-pkgs змерджили після стандартної автовалідації плюс перегляду живого модератора (New-Package завжди дивиться людина). Кілька нотаток, що зекономлять вам час:
- Маніфест я генерував через
wingetcreate— він сам робить схему, SHA256 і device-flow авторизацію, щоб відкрити PR. Набагато менше шансів помилитись, ніж писати YAML руками. - Мій NSIS-інсталер без підпису — і все одно пройшов валідацію чисто, без warning-лейблів. winget не вимагає code signing, щоб потрапити в реєстр. (SmartScreen при першому запуску — окрема історія для кінцевого юзера, не воротар winget.)
- Від подачі до мерджу — кілька днів. Бот робить основну роботу, ти переважно чекаєш на модератора.
Оновлення потім — однорядково:
wingetcreate update RomanTykhonenko.CapyBro --version <X> --urls <url> --submit
2. Баг автозапуску, який існує тільки в запакованому застосунку
Збірка для Microsoft Store запакована в MSIX. Звичайний .exe робить автозапуск класикою: пише в HKCU\Software\Microsoft\Windows\CurrentVersion\Run. Працює, так їздило роками.
Усередині MSIX-пакета це тихо перестає працювати. Записи в цей Run-ключ віртуалізуються в приватну в’юху пакета, і Windows не шанує віртуалізований Run-запис для старту при вході. Тобто юзер вмикає «Запускати при вході», застосунок слухняно пише ключ, рапортує успіх... і ніколи не стартує при наступному вході. Жодної помилки, нічого в логах.
Фікс — визначати в рантаймі, чи ти запакований, і перемикати backend:
- Детект: P/Invoke
GetCurrentPackageFullName. ПовертаєAPPMODEL_ERROR_NO_PACKAGE, коли unpackaged, і успіх — коли packaged. Один цей boolean і обирає реалізацію. - Packaged-backend: WinRT-API
Windows.ApplicationModel.StartupTask+ розширенняwindows.startupTaskу маніфесті (зuap10:Parameters, щоб передати той самий silent-прапор, що й.exe). Тепер startup-записом володіє ОС — юзер навіть може тоглити його з Параметри → Застосунки автозапуску. - Unpackaged-backend: оригінальний писач Run-ключа, незмінний.
Загорнути це за IStartupTaskBackend, який обирається рантайм-перевіркою, означало, що .exe зберіг свою точну стару поведінку, і лише Store-білд отримав новий шлях — нульовий ризик зламати те, що вже працювало.
Каверза в каверзі: підтягування WinRT-проєкції (Windows.ApplicationModel.*) піднімає TFM до чогось типу net8.0-windows10.0.17763.0 і тягне за собою чималу SDK-проєкцію. Я не хотів, щоб легкий .exe ніс це на собі, тож проєкт мультитаргетиться: звичайний Win32-білд виключає WinRT-файли через #if, і лише packaged-таргет їх компілює.
3. Нативний ARM64
Перепаковуючи, я додав нативну ARM64-збірку (win-arm64, ReadyToRun, крос-компіляція на x64-хості) другим пакетом у тій самій Store-сабмішн. WPF працює нативно на ARM64 .NET 8, тож юзери Surface / Snapdragon тепер минають шар x64-емуляції. Чесне застереження: ARM-заліза я не маю, тож воно поїхало smoke-тестованим на боці збірки, але не на реальному пристрої — якщо ви на Windows-on-ARM, я був би щиро вдячний за перевірку.
winget для
Немає коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів