Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Aby zobaczyć działający przykład end-to-end (aplikacja WPF + instalator Inno Setup), zapoznaj się z przykładem sparse-app.
Standardowy wykonywalny plik desktopowy — zbudowany przy użyciu dotnet build, MSBuild, CMake lub dowolnego innego łańcucha narzędzi — nie ma tożsamości pakietu. Bez tożsamości nie może używać wielu nowoczesnych interfejsów API Windows (wyskakujące powiadomienia, zadania w tle, udostępniać obiekty docelowe, zadania uruchamiania, interfejsy API danych aplikacji i nie tylko).
Pakietowanie rozproszone nadaje aplikacji tożsamość bez przenoszenia jej plików binarnych do pakietu MSIX. Dostarczasz niewielki pakiet zawierający wyłącznie tożsamość.msix (tylko manifest) i rejestrujesz go obok standardowo zainstalowanej aplikacji za pomocą lokalizacji zewnętrznej. Twój .exe pozostaje dokładnie tam, gdzie umieści go instalator. Jest to produkcyjny odpowiednik winapp create-debug-identity, który służy wyłącznie do debugowania na etapie programowania.
W tym przewodniku opisano trzy kroki w interfejsie wiersza polecenia, które odpowiadają pierwszym trzem krokom oficjalnego przepływu pracy Nadawanie tożsamości aplikacjom niespakietowanym:
| Krok | Polecenie | Result |
|---|---|---|
| 1. Tworzenie manifestu tożsamości | winapp init --exe <exe> --sparse |
sparse/appxmanifest.xml + sparse/Assets/ |
| 2. Kompilowanie i podpisywanie pakietu tożsamości | winapp pack <appxmanifest.xml> --cert <pfx> |
<PackageName>.identity.msix |
| 3. Osadzanie tożsamości w aplikacji | winapp embed-identity <exe> |
<msix> element w manifeście zestawiania pliku exe |
Kroki od 4 do 5 dokumentów (rejestrowanie/wyrejestrowywanie pakietu) to odpowiedzialność instalatora — zobacz Integracja instalatora.
Kiedy należy używać pakowania rozrzedliwego
- Masz już dojrzały instalator (Inno Setup, WiX, NSIS, MSI) i nie chcesz przełączać się na MSIX na potrzeby dystrybucji, ale potrzebujesz interfejsów API Windows z bramą tożsamości.
- Aplikacja musi instalować się w lokalizacji lub w układzie, na które MSIX nie zezwala.
- Potrzebujesz minimalnej zmiany addytywnej: zachowaj istniejący przepływ instalacji i dodaj jeden
.msixkrok rejestracji.
Jeśli zaczynasz od nowa i możesz dystrybuować jako MSIX, pełna spakowana aplikacja (winapp init + winapp pack <folder>) jest prostsza.
Wymagania wstępne
- Windows 10, wersja 2004 (kompilacja 19041) lub nowsza. Pakiety rozproszone opierają się na
uap10:AllowExternalContent, co wymaga wersji 19041 lub nowszej. -
Interfejs wiersza polecenia winapp — instalowanie za pomocą zestawu narzędzi (lub aktualizacja, jeśli jest już zainstalowana):
winget install Microsoft.WinApp --source winget -
Certyfikat podpisywania kodu uznawany na komputerze docelowym za zaufany. Do testów lokalnych wygeneruj certyfikat deweloperski za pomocą
winapp cert generatei zaufaj mu. Pakiety produkcyjne muszą być podpisane przy użyciu certyfikatu, którego temat pasuje do manifestuPublisher.
Walkthrough
Poniższe przykłady zakładają, że skompilowany plik wykonywalny znajduje się pod adresem ./bin/Release/net8.0-windows/MyApp.exe.
Krok 1 — Utwórz rzadki manifest tożsamości
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse
Na tej podstawie zostaną określone nazwa pakietu, wydawca, wersja i opis z pliku .exe (na podstawie informacji o wersji pliku), a następnie pojawi się monit o ich zaakceptowanie lub zastąpienie. Dodaj --use-defaults (lub --no-prompt), aby pominąć monity w CI, a --name / --publisher, aby nadpisać określone wartości:
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse --use-defaults `
--name "Contoso.MyApp" --publisher "CN=Contoso"
Domyślnie zapisuje następujące dane do dedykowanego folderu sparse/ w bieżącym katalogu (można to zmienić za pomocą --output-dir):
-
appxmanifest.xml— uproszczony manifest z elementem<uap10:AllowExternalContent>true</uap10:AllowExternalContent>(pod elementem<Properties>),ProcessorArchitecture="neutral", aplikacjąwin32Apporaz nazwą pliku exe wpisaną wExecutable. -
Assets/— zastępcze zasoby wizualne (wyodrębnione z ikony exe, gdy jest to możliwe).
sparse/Dlaczego folder, a nie obok pliku exe? Manifest iAssets/są danymi wejściowymi używanymi na etapie kompilacji przezwinapp packiwinapp embed-identity— nic nie odczytuje ich w czasie wykonywania z lokalizacji obok pliku exe (tożsamość w czasie wykonywania pochodzi z elementu<msix>osadzonego w pliku exe oraz zewnętrznej lokalizacji zarejestrowanego pakietu, a manifest odwołuje się do pliku exe według nazwy, więc jego lokalizacja jest niezależna od tego, gdzie znajduje się plik exe). Zapisywanie ich w dedykowanym folderze objętym kontrolą wersji pozwala trzymać je poza katalogiem wyjściowym kompilacji (takim jakbin/), który zostałby wyczyszczony podczas czyszczenia lub ponownej kompilacji, a także utrzymać ten folder bez plików binarnych, dzięki czemu kolejne kroki pozostają uporządkowane.winapp packiwinapp embed-identityautomatycznie szukają wsparse/, więc rzadko trzeba podawać ścieżkę.
Uwaga: Uproszczony proces inicjalizacji celowo pomija całą instalację pakietów i zestawu SDK — pakiety zawierające wyłącznie składniki tożsamości nie mają zależności od zestawu SDK.
Jeśli element appxmanifest.xml już istnieje w katalogu docelowym, init zatrzyma się, zamiast go nadpisać (wraz z jego Assets/). Uruchom ponownie za pomocą --force, aby ponownie to wygenerować.
Upewnij się, że Publisher w wygenerowanym manifeście odpowiada certyfikatowi, którego użyjesz do podpisania. Edytuj appxmanifest.xml w razie potrzeby lub przekaż --publisher podczas generowania.
Krok 2. Kompilowanie i podpisywanie pakietu tożsamości
Wskaż w winapp pack manifest rozproszony (plik, a nie katalog):
winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx
Ponieważ manifest deklaruje AllowExternalContent, winapp pack tworzy pakiet zawierający wyłącznie tożsamość.msix, zawierający tylko manifest — bez plików binarnych i bez zasobów. Dane wyjściowe są domyślnie zapisywane w bieżącym katalogu <PackageName>.identity.msix; użyj --output, aby to zmienić. Podpisywanie następuje tylko wtedy, gdy przekażesz --cert (lub --generate-cert).
Krok 3. Osadzanie tożsamości w aplikacji
Osadź element <msix>, tak aby system Windows połączył uruchomiony plik wykonywalny z pakietem tożsamości aplikacji:
# EXE mode — modify the built binary in place (uses mt.exe)
winapp embed-identity ./bin/Release/net8.0-windows/MyApp.exe
Możesz też zachować manifest side-by-side jako plik zaewidencjonowany i ponownie skompilować:
# XML mode — update an external SxS manifest, then rebuild your app
winapp embed-identity ./app.manifest
W trybie XML element <msix> jest wstawiany do manifestu docelowego (lub w nim zastępowany). Dodaj odwołanie do tego manifestu w projekcie (dla platformy .NET ustaw <ApplicationManifest>app.manifest</ApplicationManifest>) i przebuduj projekt, aby ten element został osadzony w pliku EXE.
Oba tryby odczytują tożsamość z rozrzednicy appxmanifest.xml. Po pominięciu --manifest program winapp najpierw szuka w folderze sparse/ (gdzie winapp init --exe --sparse domyślnie go zapisuje) obok elementu docelowego, następnie w bieżącym katalogu, a w ostateczności sprawdza lokalizację obok elementu docelowego i bieżący katalog; użyj --manifest, aby wskazać inną lokalizację.
Uwaga: Tryb EXE nadpisuje plik binarny przy użyciu
mt.exe, co unieważnia wszystkie istniejące podpisy Authenticode. Ponownie podpisać plik exe (np.winapp sign ./MyApp.exe <cert.pfx>) przed jego dystrybucją.
Krok 4. Rejestrowanie (na potrzeby testowania lokalnego)
Logo manifestu są pobierane z lokalizacji zewnętrznej w czasie działania, a nie z samego elementu .msix, zawierającego tylko tożsamość. Krok 1 umieścił je w katalogu ./sparse/Assets, więc skopiuj je obok pliku .exe (czyli w lokalizacji zewnętrznej) przed rejestracją — w przeciwnym razie Windows zarejestruje układ, w którym brakuje wszystkich logo, do których odwołuje się manifest:
# Copy the generated assets into the external location (beside your exe)
Copy-Item ./sparse/Assets -Destination .\bin\Release\net8.0-windows\Assets -Recurse -Force
Następnie zarejestruj pakiet tożsamości w tym folderze ( lokalizacja zewnętrzna):
Add-AppxPackage -Path .\MyApp.identity.msix `
-ExternalLocation (Resolve-Path .\bin\Release\net8.0-windows)
Uruchom aplikację i potwierdź, że tożsamość jest dostępna — na przykład Windows.ApplicationModel.Package.Current.Id.FamilyName powinno zwrócić nazwę rodziny pakietu zamiast zgłaszać wyjątek.
Aby oczyścić:
Remove-AppxPackage <full-package-name>
Obsługa zasobów
Sparse .msix jest wyłącznie typu identity. Zasoby wizualne, do których odwołuje się manifest (Assets\StoreLogo.png, kafelki itp.), są pobierane z lokalizacji zawartości zewnętrznej w czasie wykonywania aplikacji — tj. z katalogu instalacyjnego aplikacji — .msix z wnętrza pliku .
Oznacza to, że należy umieścić folder Assets/ obok aplikacji (w układzie oczekiwanym przez manifest, względem lokalizacji zewnętrznej).
Krok 2 pakuje bezpośrednio plik manifestu (winapp pack ./sparse/appxmanifest.xml), co tworzy .msix zawierające wyłącznie tożsamość na podstawie samego manifestu — pozostałe pliki w tym samym katalogu są ignorowane, więc pakiet nigdy nie zawiera zasobów ani plików binarnych. (Jeśli zamiast tego wskażesz winapp packfolder , którego manifest deklaruje AllowExternalContent, ostrzega o wszelkich odnalezionych zasobach lub plikach binarnych, ponieważ w przypadku rozrzednego pakietu należą one do lokalizacji zewnętrznej, a nie wewnątrz .msixpliku ).
Integracja instalatora
Rejestracja i wyrejestrowanie to zadanie instalatora. Wzorzec jest taki sam w przypadku narzędzi instalatora:
-
Zainstaluj: skopiuj pliki binarne aplikacji,
Assets/folder i.msixfolder do katalogu instalacji, a następnie uruchom polecenieAdd-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>". -
Odinstaluj: uruchom polecenie
Remove-AppxPackage <full-package-name>przed usunięciem plików.
Zabezpieczenia: katalog instalacyjny jest rozpoznawany w czasie instalacji i może zawierać znaki (np. pojedynczy cudzysłów), które przerywają literał ciągu programu PowerShell. Zawsze należy unikać lub weryfikować ścieżkę przed interpolowaniem jej do
-Commandciągu — fragmenty kodu WiX i NSIS poniżej zakładają zaufaną ścieżkę instalacji, podczas gdy przykład instalacji Inno demonstruje bezpieczne ucieczki. Preferuj przekazywanie ścieżek jako argumentów do skryptu-Fileza pośrednictwem interpolacji wbudowanej-Command.
Inno Setup
Skompiluj argumenty programu PowerShell w [Code] funkcji, aby ścieżka instalacji środowiska uruchomieniowego została uniknięta dla pojedynczego cudzysłowu programu PowerShell (katalog instalacyjny zawierający ' skrypt nie może być w stanie wstrzyknąć skryptu):
[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;
Zobacz przykład setup.iss, aby uzyskać kompletny, działający .
Poniższe przykłady WiX i NSIS wywołują niewielki register-sparse.ps1 za pośrednictwem -File, dzięki czemu ścieżka instalacji jest przekazywana jako parametr (PowerShell wiąże go jako dane), zamiast interpolować ją w ciągu -Command. Pozwala to uniknąć iniekcji skryptu za pośrednictwem spreparowanego katalogu instalacyjnego (np. nazwy folderu zawierającego cudzysłów lub $(...)):
# 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 (wersja 3)
Zarejestruj dla każdego użytkownika (Impersonate="yes"), ponieważ Add-AppxPackage rejestruje pakiet dla konta, na którym jest uruchomiony. Odroczona akcja uruchamiana jako LocalSystem z Impersonate="no"nie nadaje tożsamości instalującemu użytkownikowi (i często jest odrzucana). W przypadku pakietu MSI instalowanego na poziomie komputera uruchom rejestrację z personifikacją, aby została zastosowana do użytkownika wywołującego.
Odroczona akcja niestandardowa nie może bezpośrednio odczytać INSTALLFOLDER (odroczone akcje są wykonywane w kontekście bez dostępu do właściwości), a samo zadeklarowanie akcji nie powoduje jej uruchomienia. Dlatego przekaż ścieżki za pomocą CustomActionData — natychmiastowej akcji typu 51, której nazwa Property jest taka sama jak nazwa odroczonej akcji Id — i zaplanuj obie po 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 jest zawarty w rozszerzeniu narzędziowym WiX (WixUtilExtension); dodaj do niego odwołanie, aby plik binarny WixCA był dostępny.
Pojedyncza akcja wykonywana z personifikacją rejestruje tożsamość tylko dla użytkownika uruchamiającego instalator. Aby udostępnić instalację dla całego komputera każdemu użytkownikowi, zamiast tego zarejestruj ją przy pierwszym uruchomieniu (dla użytkownika) lub użyj mechanizmu aprowizacji, takiego jak
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
Rozwiązywanie problemów
Package.Current zwraca błąd / „brak tożsamości pakietu” podczas działania
- Pakiet identyfikatora nie jest zarejestrowany lub w manifeście fusion pliku exe brakuje elementu
<msix>. Uruchom ponownie (i ponownie skompilujwinapp embed-identity, jeśli używasz trybu XML), a następnie ponownie zarejestruj się przy użyciu poleceniaAdd-AppxPackage -ExternalLocation. - Element
<msix packageName>/ /publisherapplicationIdw pliku exe musi dokładnie odpowiadać tożsamości zarejestrowanego pakietu.
Elementy zawartości/logo nie są wyświetlane
- Upewnij się, że folder
Assets/został wdrożony w lokalizacji zewnętrznej z zachowaniem tych samych ścieżek względnych, których oczekuje manifest. Zasoby są rozpoznawane z lokalizacji zewnętrznej, a nie z.msix.
Add-AppxPackage kończy się błędem podpisu lub zaufania
- Element
.msixmusi być podpisany certyfikatem, któremu ufa komputer i którego podmiot jest zgodny z elementemPublishermanifestu. Do testów lokalnych wygeneruj certyfikat deweloperski i dodaj go do zaufanych za pomocą poleceniawinapp cert generate, a następnie upewnij się, że manifestPublisherjest z nim zgodny.
MakeAppx: "Aplikacja z wartością RuntimeBehavior „win32App” nie może deklarować punktu wejścia EntryPoint"
- Aplikacja sparse
win32Appnie może deklarowaćEntryPoint. Manifesty wygenerowane przezwinapp init --sparseprogram są już poprawne. Usuń dowolnyEntryPointatrybut, jeśli ręcznie edytowano manifest.
"Dane wejściowe są plikiem, ale nie rozrzednym manifestem"
-
winapp pack <file>Akceptuje tylko manifest, który deklaruje<uap10:AllowExternalContent>true</uap10:AllowExternalContent>. Wygeneruj go za pomocą , lub przekaż wejściowywinapp init --exe <exe> --sparse, aby zbudować pełny pakiet MSIX.