Подводные камни Store #4: .NET Trimming обрезал привязки WinUI 3
2026-05-31
Tags: Windows · Microsoft Store · Подводные камни Store
Эта проблема возникла ещё на раннем этапе. Недавно, пока писал серию про подводные камни, наткнулся на старое письмо с отказом — надеюсь, эта запись поможет разработчикам, которые только начинают разбираться с процессом публикации приложений WinUI 3 / .NET.
Что произошло
Моё приложение WinUI 3 было отправлено на сертификацию в Microsoft Store. Отчёт о сертификации вернулся со следующим:
Status: Attention needed
10.1.2.10 Functionality
Unusable Feature: "Download to local cache", "Open File" - The product fails to download copied items to local cache and "Open File" button in the product is not usable
Подождите — я же тестировал локально, и «Скачать в локальный кэш» и «Открыть файл» работали нормально. Почему они не работали на тестовой машине Microsoft?
Расследование
Самое подозрительное отличие: локально я использовал сборку Debug, а в Store отправлялась сборка Release. Проверив файл .csproj, я обнаружил ключевое различие между конфигурациями Debug и Release:
<!-- Debug -->
<PublishTrimmed>False</PublishTrimmed>
<!-- Release -->
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>В сборке Release был включён .NET Trimming.
Что такое .NET Trimming
.NET Trimming — это функция оптимизации в SDK .NET. При публикации приложения триммер выполняет статический анализ, чтобы найти сборки, типы и методы, на которые нет прямых ссылок в коде, и удаляет их для уменьшения размера приложения.
Проблема в том, что триммер видит только статические ссылки. Если ваш код вызывает тип через рефлексию во время выполнения, а при компиляции нет никаких статических ссылок на него, триммер считает, что тип «не используется», и удаляет его.
Почему WinUI 3 особенно уязвим
WinUI 3 и его уровень взаимодействия WinRT в значительной степени полагаются на рефлексию и динамические возможности. Традиционные {Binding}, конвертеры значений и рефлексия метаданных взаимодействия WinRT — всё это создаёт вызовы времени выполнения, которые статический анализ триммера не может отследить. Когда триммер удаляет модели данных, команды, конвертеры и другие типы, необходимые для привязки, считая их «неиспользуемыми», привязки не могут найти свои цели во время выполнения — кнопки перестают работать, текст перестаёт обновляться. Без ошибок, без падений — просто перестаёт работать.
Решение
Самый простой способ: отключить тримминг в .csproj:
- <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
+ <PublishTrimmed>False</PublishTrimmed>Пересобрал и отправил заново — сертификация пройдена.
Если вам действительно нужен тримминг для уменьшения размера пакета, можно обеспечить совместимость следующими способами:
- Пометить нужные типы атрибутами
[DynamicDependency]или[DynamicallyAccessedMembers], указав триммеру сохранить их - Настроить
<TrimmerRootAssembly>в.csprojдля сохранения целой сборки
Но для большинства приложений WinUI 3 прямое отключение тримминга — самый простой вариант.
Итоги
- .NET Trimming удаляет код, который он считает «неиспользуемым», но не распознаёт вызовы через рефлексию
- Привязка данных WinUI 3 зависит от рефлексии и легко может быть обрезана по ошибке
- Если приложение WinUI 3 не проходит сертификацию Store с сообщением о «неработоспособной функции», но локально работает нормально, первое, что нужно проверить — это тримминг
- Сборки Debug по умолчанию не используют тримминг, а сборки Release могут его включать — всегда тестируйте сборки Release/Publish перед отправкой в Store
- В ближайшее время мы опубликуем статью с советами по тестированию перед отправкой в Store — следите за обновлениями
Эта статья входит в серию Подводные камни Store.