Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Пример работающего сквозного решения (WPF-приложение + установщик Inno Setup) см. в образце sparse-app.
Стандартный исполняемый файл рабочего стола, созданный с помощью dotnet buildMSBuild, CMake или любой другой цепочки инструментов, не имеет удостоверения пакета. Без удостоверения он не может использовать множество современных API-интерфейсов Windows (всплывающие уведомления, фоновые задачи, общие целевые объекты, задачи запуска, API данных приложения и многое другое).
Неполная упаковка присваивает приложению идентичность без переноса его двоичных файлов в пакет MSIX. Вы поставляете небольшой пакет только с удостоверяющими данными.msix (то есть только манифест) и регистрируете его вместе с вашим приложением, установленным обычным способом, используя внешнее расположение. Ваш .exe остаётся именно там, куда его помещает программа установки. Это рабочий аналог winapp create-debug-identity, который предназначен только для отладки во время разработки.
В этом руководстве рассматриваются три шага CLI, которые соответствуют первым трем шагам официального процесса Предоставление удостоверения непакетированным приложениям:
| Шаг | Command | Result |
|---|---|---|
| 1. Создайте манифест идентификации | winapp init --exe <exe> --sparse |
sparse/appxmanifest.xml + sparse/Assets/ |
| 2. Создайте и подпишите пакет идентификации | winapp pack <appxmanifest.xml> --cert <pfx> |
<PackageName>.identity.msix |
| 3. Внедрение удостоверения в приложение | winapp embed-identity <exe> |
<msix> элемент в манифесте Fusion EXE-файла |
Шаги 4–5 документации (регистрация и отмена регистрации пакета) являются ответственностью установщика — см. интеграцию установщика.
Когда следует использовать разреженную упаковку
- У вас уже есть зрелый установщик (Inno Setup, WiX, NSIS, MSI), и вы не хотите переходить на MSIX для распространения приложения, но вам нужны API Windows, требующие наличия идентичности приложения.
- Приложение должно устанавливаться в расположение или иметь схему размещения, которую MSIX не поддерживает.
- Требуется минимальное, аддитивное изменение: сохраните существующий поток установки и добавьте один
.msixшаг регистрации.
Если вы начинаете с нуля и можете распространять приложение в формате MSIX, полностью упакованное приложение (winapp init + winapp pack <folder>) — более простой вариант.
Необходимые условия
- Windows 10 версии 2004 (сборка 19041) или более поздняя. Разреженные пакеты зависят от
uap10:AllowExternalContent, для которого требуется версия 19041 и выше. -
winapp CLI — установка с помощью winget (или обновление, если оно уже установлено):
winget install Microsoft.WinApp --source winget -
Сертификат подписи кода, которому доверяют на целевом компьютере. Для локального тестирования создайте сертификат
winapp cert generateразработки и доверяйте ему. Рабочие пакеты должны быть подписаны сертификатом, субъект которого соответствует манифестуPublisher.
Walkthrough
В приведенных ниже примерах предполагается, что встроен исполняемый файл ./bin/Release/net8.0-windows/MyApp.exe.
Шаг 1 — Создайте разреженный манифест идентификации
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse
Это определяет имя пакета, издатель, версию и описание из exe -файла (с помощью сведений о версии файла) и предлагает принять или переопределить их. Добавьте --use-defaults (или --no-prompt), чтобы пропустить запросы в CI, а --name / --publisher — чтобы переопределить конкретные значения:
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse --use-defaults `
--name "Contoso.MyApp" --publisher "CN=Contoso"
По умолчанию следующее записывается в отдельную папку sparse/ в текущем каталоге (можно переопределить с помощью --output-dir):
-
appxmanifest.xml— минимальный манифест с<uap10:AllowExternalContent>true</uap10:AllowExternalContent>(элементом в<Properties>),Executable, приложениемProcessorArchitecture="neutral"и именем exe, подставленным вwin32App. -
Assets/— визуальные ресурсы-заполнители (по возможности извлечённые из значка EXE).
Почему
sparse/папка, а не рядом с exe-файлом? Манифест иAssets/используются как входные данные на этапе сборки дляwinapp packиwinapp embed-identity— ничто не считывает их из каталога рядом с exe-файлом во время выполнения (идентичность во время выполнения берется из элемента<msix>, встроенного в exe, а также из внешнего расположения зарегистрированного пакета, а манифест ссылается на exe-файл по имени, поэтому расположение самого манифеста не зависит от того, где находится exe-файл). Запись этих файлов в отдельную папку под управлением системы контроля версий не даёт им попасть в каталог результатов сборки (например,bin/), который будет очищен при clean/rebuild, и оставляет эту папку без двоичных файлов, чтобы следующие шаги тоже выполнялись в чистой среде.winapp packиwinapp embed-identityавтоматически ищут вsparse/, поэтому вам редко нужно указывать путь.
Примечание: Облегчённый процесс инициализации намеренно пропускает установку SDK и пакетов — пакеты, связанные только с идентификацией, не зависят от SDK.
Если appxmanifest.xml уже существует в целевом каталоге, init прекращает работу, а не перезаписывает его (и его Assets/). Повторно выполните команду с --force, чтобы создать его заново.
Убедитесь, что Publisher в созданном манифесте соответствует сертификату, которым вы будете подписывать. При необходимости измените appxmanifest.xml или передайте --publisher при генерации.
Шаг 2. Создание и подписание пакета удостоверений
Наведите указатель winapp pack на разреженный манифест (файл, а не папку):
winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx
Так как манифест объявляет AllowExternalContent, создает .msixwinapp pack, содержащий только манифест, — без двоичных файлов и ресурсов. По умолчанию выходные данные сохраняются в <PackageName>.identity.msix в текущем каталоге; чтобы изменить это, используйте --output. Подпись выполняется, только если передать --cert (или --generate-cert).
Шаг 3. Внедрение удостоверения в приложение
Внедрите элемент <msix>, чтобы Windows связывала запущенный exe-файл с пакетом идентификации:
# EXE mode — modify the built binary in place (uses mt.exe)
winapp embed-identity ./bin/Release/net8.0-windows/MyApp.exe
Или сохраняйте параллельный манифест в виде проверенного файла и перестроения:
# XML mode — update an external SxS manifest, then rebuild your app
winapp embed-identity ./app.manifest
В режиме XML элемент <msix> вставляется в целевой манифест (или заменяет существующий элемент в нём). Добавьте ссылку на этот манифест в своём проекте (для .NET укажите <ApplicationManifest>app.manifest</ApplicationManifest>) и пересоберите проект, чтобы этот элемент был встроен в EXE-файл.
Оба режима считывают идентификатор из разреженного appxmanifest.xml. Если параметр --manifest опущен, winapp сначала ищет в папке sparse/ (куда winapp init --exe --sparse записывает его по умолчанию) рядом с целевым объектом, затем в текущем каталоге, а в противном случае ищет рядом с целевым объектом и в текущем каталоге; укажите --manifest, чтобы задать другой путь.
Примечание: Режим EXE перезаписывает двоичный файл, добавляя
mt.exe, что делает недействительной любую существующую подпись Authenticode. Повторно подпишите exe (например,winapp sign ./MyApp.exe <cert.pfx>) перед распространением.
Шаг 4. Регистрация (для локального тестирования)
Логотипы манифеста загружаются из внешнего местоположения во время выполнения, а не из содержащего только идентификатор .msix. Шаг 1 записал их в ./sparse/Assets, поэтому перед регистрацией скопируйте их рядом с вашим EXE-файлом (во внешнее расположение) — в противном случае Windows зарегистрирует раскладку, в которой отсутствуют все логотипы, на которые ссылается манифест:
# Copy the generated assets into the external location (beside your exe)
Copy-Item ./sparse/Assets -Destination .\bin\Release\net8.0-windows\Assets -Recurse -Force
Затем зарегистрируйте пакет идентификации для этой папки (внешнее местоположение):
Add-AppxPackage -Path .\MyApp.identity.msix `
-ExternalLocation (Resolve-Path .\bin\Release\net8.0-windows)
Запустите приложение и подтвердите, что идентификатор приложения присутствует — например, Windows.ApplicationModel.Package.Current.Id.FamilyName должен возвращать имя семейства вашего пакета вместо того, чтобы вызывать исключение.
Чтобы очистить:
Remove-AppxPackage <full-package-name>
Обработка ресурсов
Разреженный .msix — только для идентификации. Визуальные ресурсы, на которые ссылается манифест (Assets\StoreLogo.png, плитки и т. д.), загружаются из внешнего расположения контента во время выполнения — то есть из каталога установки вашего приложения, — не из .msix.
Это означает, что необходимо разместить папку Assets/ рядом с приложением (в той же структуре, которую ожидает манифест, относительно внешнего местоположения).
Шаг 2 напрямую упаковывает файл манифеста (winapp pack ./sparse/appxmanifest.xml), при этом создается .msix только с удостоверением на основе лишь этого манифеста — соседние файлы игнорируются, поэтому в него никогда не входят ни ваши ресурсы, ни двоичные файлы. (Если вместо этого указать winapp pack на папку, в манифесте которой указано AllowExternalContent, будет выдано предупреждение о любых ресурсах или двоичных файлах, которые будут в ней найдены, так как для разреженного пакета они должны находиться во внешнем расположении, а не внутри .msix.)
Интеграция установщика
Регистрация и отмена регистрации — это задание установщика. Шаблон одинаков для средств установщика:
-
Установите: скопируйте бинарные файлы приложения, папку
Assets/и.msixв каталог установки, затем запуститеAdd-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>". -
Удаление: перед удалением файлов запустите
Remove-AppxPackage <full-package-name>.
Безопасность: каталог установки определяется в процессе установки и может содержать символы (например, одинарную кавычку), которые позволяют выйти за пределы строкового литерала PowerShell. Всегда экранируйте или проверьте путь перед интерполяцией в
-Commandстроку — фрагменты кода WiX и NSIS ниже предполагают надежный путь установки, а в примере установки Inno демонстрируется безопасный обход. Предпочтительно передавать пути как аргументы в скрипт-File, а не использовать встроенную интерполяцию-Command.
Настройка Inno
Создайте аргументы PowerShell в функции [Code], чтобы путь установки среды выполнения экранировался для литерала PowerShell в одинарных кавычках (каталог установки, содержащий ', не должен позволять внедрить скрипт):
[Files]
Source: "dist\*"; DestDir: "{app}"; Flags: recursesubdirs
Source: "MyApp.identity.msix"; DestDir: "{app}"
[Run]
Filename: "powershell.exe"; Parameters: "{code:RegisterParams}"; Flags: runhidden
[UninstallRun]
Filename: "powershell.exe"; \
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Get-AppxPackage -Name 'MyApp' | Remove-AppxPackage"""; \
Flags: runhidden
[Code]
function EscapePSLiteral(const Value: string): string;
var S: string;
begin
S := Value; StringChange(S, '''', ''''''); Result := S;
end;
function RegisterParams(Param: string): string;
var AppDir: string;
begin
AppDir := ExpandConstant('{app}');
{ -ErrorAction Stop + try/catch make a registration failure terminating, so powershell.exe
exits nonzero and the AfterInstall callback (see the full sample) can abort with rollback. }
Result := '-NoProfile -ExecutionPolicy Bypass -Command "try { Add-AppxPackage -Path ''' +
EscapePSLiteral(AppDir + '\MyApp.identity.msix') +
''' -ExternalLocation ''' + EscapePSLiteral(AppDir) + ''' -ErrorAction Stop } catch { Write-Error $_; exit 1 }"';
end;
См. пример setup.iss для получения полного рабочего .
Приведённые ниже примеры WiX и NSIS вызывают небольшой register-sparse.ps1 через -File, чтобы путь установки передавался как параметр (PowerShell обрабатывает его как данные), а не интерполировался в строку -Command. Это позволяет избежать внедрения скриптов с помощью созданного каталога установки (например, имени папки, содержащей кавычки или $(...)):
# register-sparse.ps1 — ship this alongside your installer
param(
[Parameter(Mandatory)] [string] $MsixPath,
[Parameter(Mandatory)] [string] $ExternalLocation,
[Parameter(Mandatory)] [string] $PackageName
)
$ErrorActionPreference = 'Stop'
try {
# Add-AppxPackage emits NON-terminating errors by default, so a failure would otherwise leave
# the process exit code at 0 and let the installer complete without identity. Try the add
# directly first: a fresh install or a version-bumped upgrade registers/updates in place
# without touching any existing registration. -ErrorAction Stop + the outer trap make a real
# failure terminating so the installer (WiX Return="check" / NSIS) sees it.
try {
Add-AppxPackage -Path $MsixPath -ExternalLocation $ExternalLocation -ErrorAction Stop
} catch {
# Only ONE failure is safe to resolve by unregister+retry: the exact same version is already
# registered (HRESULT 0x80073CFB, ERROR_PACKAGE_ALREADY_EXISTS — "already installed,
# reinstallation blocked"), which Add-AppxPackage rejects. Re-throw everything else
# (untrusted/corrupt .msix, unsupported OS, ...) so a bad new package can NEVER unregister a
# working prior registration and strip the installed app of the identity it already had.
if ($_.Exception.HResult -ne 0x80073CFB) { throw }
Get-AppxPackage -Name $PackageName | Remove-AppxPackage -ErrorAction SilentlyContinue
Add-AppxPackage -Path $MsixPath -ExternalLocation $ExternalLocation -ErrorAction Stop
}
} catch {
Write-Error $_
exit 1
}
WiX (v3)
Регистрируйте для пользователя (Impersonate="yes"), так как Add-AppxPackage регистрирует пакет для учетной записи, под которой он выполняется. Отложенное действие с Impersonate="no" выполняется как LocalSystem, что не предоставляет удостоверение личности устанавливающему пользователю (и обычно отклоняется). Для MSI-пакета с установкой для всех пользователей выполните регистрацию в режиме олицетворения, чтобы она применялась к пользователю, инициировавшему запуск.
Отложенное пользовательское действие не может прочитать INSTALLFOLDER напрямую (отложенные действия выполняются в контексте без доступа к свойствам), и само по себе объявление действия не запускает его. Таким образом, передайте пути через CustomActionData — немедленное действие типа 51, чьё имя Property совпадает с именем отложенного действия Id — и запланируйте оба после InstallFiles:
<!-- Immediate: stash the command line (with the resolved paths) into the deferred action's
CustomActionData. Windows Installer copies the value of the property named the same as a
deferred action into that action's CustomActionData. -->
<CustomAction Id="SetRegisterSparseCmd" Property="RegisterSparse" Execute="immediate"
Value="powershell.exe -NoProfile -ExecutionPolicy Bypass -File "[INSTALLFOLDER]register-sparse.ps1" -MsixPath "[INSTALLFOLDER]MyApp.identity.msix" -ExternalLocation "[INSTALLFOLDER]" -PackageName "MyPackageIdentityName"" />
<!-- Deferred + impersonated: CAQuietExec reads its command line from CustomActionData when run
deferred, so it registers the package for the invoking user. Return="check" fails the
install if registration fails. -->
<CustomAction Id="RegisterSparse" BinaryKey="WixCA" DllEntry="CAQuietExec"
Execute="deferred" Impersonate="yes" Return="check" />
<InstallExecuteSequence>
<Custom Action="SetRegisterSparseCmd" After="InstallFiles">NOT Installed</Custom>
<Custom Action="RegisterSparse" After="SetRegisterSparseCmd">NOT Installed</Custom>
</InstallExecuteSequence>
CAQuietExec поставляется в расширение WiX util (WixUtilExtension); ссылается на него, чтобы двоичный WixCA файл был доступен.
Одно действие, выполняемое от имени другого пользователя, регистрирует данные пользователя только для пользователя, запускающего установщик. Чтобы выполнить предварительную настройку для каждого пользователя при установке для всех пользователей компьютера, вместо этого выполняйте регистрацию при первом запуске (для каждого пользователя) или используйте механизм предварительной настройки, например
Add-AppxProvisionedPackage.
NSIS
Section
# Capture the PowerShell exit code and abort if registration failed. register-sparse.ps1 exits
# nonzero on failure (it sets $ErrorActionPreference='Stop' and traps), so without this check the
# installer would complete even though the app has no identity.
ExecWait 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\register-sparse.ps1" -MsixPath "$INSTDIR\MyApp.identity.msix" -ExternalLocation "$INSTDIR" -PackageName "MyPackageIdentityName"' $0
IntCmp $0 0 +2
Abort "Registering the sparse identity package failed (exit code $0). The app requires package identity."
SectionEnd
Устранение неполадок
Package.Current выдаёт ошибку / "отсутствует идентификатор пакета" при выполнении
- Пакет идентификации не зарегистрирован, или в манифесте fusion EXE-файла отсутствует элемент
<msix>. Запустите повторноwinapp embed-identity(и выполните повторную сборку, если используете режим XML), затем повторно зарегистрируйтесь черезAdd-AppxPackage -ExternalLocation. - Элемент
<msix packageName>/ /publisherapplicationIdв exe-файле должен точно совпадать с идентификатором зарегистрированного пакета.
Ресурсы и логотипы не отображаются
-
Assets/Убедитесь, что папка развернута во внешнем расположении с теми же относительными путями, которые ожидает манифест. Ресурсы загружаются из внешнего местоположения, а не из.msix.
Add-AppxPackageсбой из-за ошибки подписи или доверия
-
.msixдолжен быть подписан сертификатом, которому доверяют на компьютере и субъект которого соответствует манифестуPublisher. Для локального тестирования сгенерируйте и сделайте доверенным сертификат разработчика с помощьюwinapp cert generate, а затем убедитесь, что манифестPublisherсоответствует этому сертификату.
MakeAppx: "Приложение со значением RuntimeBehavior "win32App" не должно объявлять EntryPoint.
- Приложение с разрежёнными ресурсами
win32Appне должно объявлятьEntryPoint. Манифесты, созданные с помощьюwinapp init --sparse, уже правильны; удалите атрибутEntryPoint, если вы редактировали манифест вручную.
"Входные данные — это файл, но не разреженный манифест"
-
winapp pack <file>принимает только манифест, объявляющий<uap10:AllowExternalContent>true</uap10:AllowExternalContent>. Сгенерируйте его с помощьюwinapp init --exe <exe> --sparse, или передайте на вход папку folder, чтобы создать полный пакет MSIX.
См. также
Windows developer