Csomagidentitás hozzárendelése ritka csomagolás használatával

Egy szabványos, MSBuild, CMake vagy más eszközlánc használatával készült asztali végrehajtható fájl nem rendelkezik csomagidentitással dotnet build. Identitás nélkül az alkalmazás nem használhat számos modern Windows API-t, például bejelentési értesítéseket, háttérfeladatokat, megosztási célokat, indítási feladatokat és alkalmazásadatok API-kat.

A ritka csomagolás identitást biztosít az alkalmazásnak anélkül, hogy bináris fájljait MSIX-be helyezné. Létrehoz egy apró, csak identitást tartalmazó .msix elemet, amely csak egy jegyzékfájlt tartalmaz, majd egy külső hely használatával regisztrálja azt a szokásos módon telepített alkalmazás mellett. Az Ön .exe pontosan ott marad, ahová a telepítő helyezi.

Ebben a cikkben létrehoz egy csak identitásalapú csomagot, beágyazza az identitáshivatkozást az alkalmazásba, regisztrálja a csomagot a helyi teszteléshez, és integrálja a regisztrációt a telepítőbe.

Prerequisites

  • Windows 10, 2004-es (19041-es build) vagy újabb verzió. A szórt csomagok a(z) uap10:AllowExternalContent elemre támaszkodnak, amelyhez 19041-es vagy újabb build szükséges.

  • Egy terminál, például Windows PowerShell vagy Windows terminál, a jelen cikkben szereplő parancsok futtatásához.

  • A winapp parancssori felület. Telepítse vagy frissítse a terminálból a winget használatával:

    winget install Microsoft.winappcli --source winget
    
  • A célgépen megbízhatónak minősülő kódaláíró tanúsítvány. A helyi teszteléshez hozzon létre egy fejlesztési tanúsítványt, winapp cert generate és bízzon benne. A kiadási csomagok aláírása olyan tanúsítvánnyal, amelynek alanya megegyezik a manifesztfájllal Publisher.

A ritka csomagolás működése

A ritka csomagolás a gyártási megfelelője winapp create-debug-identity, amely csak a fejlesztői időalapú hibakereséshez használható. Csomagidentitást biztosít egy olyan alkalmazásnak, amelyet ön a saját telepítőjével terjeszt, így az alkalmazás identitásalapú Windows API-kat hívhat meg.

A jelen cikk parancssori felületének lépései az identitás megadása nem csomagolt alkalmazások munkafolyamatának első három lépésére mutatnak:

Lépés Parancs Result
1. Az identitásjegyzék létrehozása winapp init --exe <exe> --sparse sparse/appxmanifest.xml + sparse/Assets/
2. Az identitáscsomag létrehozása és aláírása winapp pack <appxmanifest.xml> --cert <pfx> <PackageName>.identity.msix
3. Identitás beágyazása az alkalmazásba winapp embed-identity <exe> <msix> elem az exe fúziós jegyzékében

A munkafolyamat 4. lépése – a csomag regisztrálása és regisztrációjának törlése – a telepítő felelőssége; az 5. lépés nem kötelező. Ez a cikk bemutatja, hogyan regisztrálhat a helyi tesztelésre, és hogyan integrálhatja a regisztrációt a telepítőbe.

Használjon ritka csomagolást, ha:

  • Már rendelkezik kiforrott telepítővel (Inno Setup, WiX, NSIS, MSI), és nem szeretne msIX-re váltani a disztribúcióhoz, de identitásalapú Windows API-kra van szüksége.
  • Az alkalmazásnak olyan elérési útra vagy elrendezésre kell telepítenie, amelyet az MSIX nem engedélyez.
  • Minimális, additív módosítást szeretne: tartsa meg a meglévő telepítési folyamatot, és adjon hozzá egy .msix regisztrációs lépést.

Ha most kezdi, és MSIX-ként tudja terjeszteni, a teljesen csomagolt alkalmazás (winapp init + winapp pack <folder>) egyszerűbb.

Egy teljes, működőképes, elejétől a végéig bemutatott példáért (egy WPF-alkalmazás Inno Setup telepítővel) lásd a sparse-app mintát.

Az alábbi példák egy beépített végrehajtható fájlt feltételeznek a következő helyen ./bin/Release/net8.0-windows/MyApp.exe: .

Ritkított identitásmanifest létrehozása

Hozza létre a ritka jegyzékfájlt a beépített végrehajtható fájlból:

winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse

Ez a parancs a csomag nevét, közzétevőjét, verzióját és leírását az exe fájlverziójának adataiból következteti, és kéri, hogy fogadja el vagy bírálja felül őket. A CI-ben megjelenő kérdések kihagyásához adja meg a --use-defaults (vagy --no-prompt) elemet, konkrét értékek felülírásához pedig a --name vagy --publisher elemet:

winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse --use-defaults `
  --name "Contoso.MyApp" --publisher "CN=Contoso"

Alapértelmezés szerint a parancs a következőket írja az aktuális könyvtár egy dedikált sparse/ mappájába (felülbírálás a következővel --output-dir):

  • appxmanifest.xml — egy egyszerű manifest, benne <uap10:AllowExternalContent>true</uap10:AllowExternalContent>-val (egy elem a(z) <Properties> alatt), ProcessorArchitecture="neutral"-mal, egy win32App alkalmazással, és az exe neve a(z) Executable mezőbe beírva.
  • Assets/ — helyőrző vizualizációs objektumok, lehetőség szerint kinyerve az exe ikonjából.

A manifestum és Assets/ olyan fordításkori bemenetek, amelyeket a winapp pack és a winapp embed-identity használ fel. Futásidőben semmi nem olvassa őket az exe mellől, ezért egy dedikált, verziókövetett sparse/ mappa megakadályozza, hogy egy buildkimeneti könyvtárba (például bin/) kerüljenek, amelyet egy tisztítás vagy újraépítés törölne. winapp pack és winapp embed-identity automatikusan a(z) sparse/ helyen keres, ezért ritkán kell megadnia az elérési utat.

Megjegyzés:

A ritka init folyamat szándékosan kihagyja az összes SDK-t és csomagtelepítést. A csak identitásalapú csomagok nem rendelkeznek SDK-függőségekkel.

Ha egy appxmanifest.xml már létezik a célkönyvtárban, az init leáll ahelyett, hogy felülírná azt (és a Assets/ elemét). Az újrageneráláshoz futtassa újra a(z) --force használatával. Győződjön meg arról, hogy a generált jegyzékfájlban lévő Publisher megegyezik azzal a tanúsítvánnyal, amellyel aláírja — szükség esetén szerkessze a(z) appxmanifest.xml elemet, vagy adja meg a(z) --publisher paramétert a generáláskor.

Az identitáscsomag létrehozása és aláírása

Mutasson winapp pack a ritka jegyzékre (fájlra, nem mappára):

winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx

Mivel a manifest deklarálja a AllowExternalContent-t, a winapp pack egy csak identitást tartalmazó .msix-t hoz létre, amely csak a manifestet tartalmazza – binárisok és erőforrások nélkül. A jegyzék mellett lévő testvérfájlokat a rendszer figyelmen kívül hagyja, így a csomag soha nem tartalmazza az objektumokat vagy bináris fájlokat. A kimenet alapértelmezés szerint a jelenlegi könyvtárban lévő <PackageName>.identity.msix értéket használja; ennek módosításához használja a(z) --output kapcsolót. Az aláírásra csak akkor kerül sor, ha megadja a --cert elemet (vagy a --generate-cert elemet).

Identitás beágyazása az alkalmazásba

Ágyazza be az <msix> elemet, hogy Windows csatlakoztassa a futó exe-t az identitáscsomaghoz. Módosíthatja a beépített bináris fájlt, vagy fenntarthat egy beadott, egymás melletti jegyzékfájlt.

A beépített bináris fájl közvetlen módosítása:

# EXE mode — modify the built binary in place
winapp embed-identity ./bin/Release/net8.0-windows/MyApp.exe

A side-by-side jegyzékfájl verziókövetett fájlként való megőrzéséhez és újraépítéséhez:

# XML mode — update an external SxS manifest, then rebuild your app
winapp embed-identity ./app.manifest

XML módban az <msix> elem be lesz szúrva a céljegyzékbe (vagy be van cserélve). Hivatkozzon a projektben lévő jegyzékfájlra (.NET esetén állítsa be: <ApplicationManifest>app.manifest</ApplicationManifest>), majd építse újra, hogy az elem beágyazódjon az EXE fájlba.

Mindkét mód egy ritkás appxmanifest.xml elemből olvassa ki az azonosítót. Ha kihagyja --manifest, a winapp először a cél melletti mappában sparse/ , majd az aktuális könyvtárban jelenik meg. Adja át a(z) --manifest elemet, hogy máshová mutasson.

Megjegyzés:

Az EXE mód újraírja a bináris fájlt, amely érvényteleníti a meglévő Authenticode-aláírásokat. A terjesztés előtt írja alá újra az exe-t (például winapp sign ./MyApp.exe --cert ./devcert.pfx --cert-password <certificate-password>).

Az identitáscsomag regisztrálása helyi teszteléshez

A regisztráció a munkafolyamat 4. lépésének része. Éles környezetben a telepítő végrehajtja ezt a lépést; helyi teszteléshez futtassa saját maga.

A regisztráció előtt győződjön meg arról, hogy a csomaggal aláírt tanúsítvány megbízható ezen a gépen – Windows elutasítja a nem megbízható tanúsítvány által aláírt identitáscsomagot. Helyi fejlesztéshez nyissa meg a PowerShellt vagy a Windows terminál rendszergazdaként, keresse meg a munkakönyvtárat, és telepítse a létrehozott PFX-tanúsítványt a helyi gép Megbízható személyek tárolójába:

winapp cert install .\devcert.pfx

Warning

Csak a saját gépén végzett helyi fejlesztéshez bízzon meg egy tanúsítványban. Ne szállítsa vagy bízza meg az önaláírt fejlesztési tanúsítványt a felhasználói gépeken. Éles csomagokat aláírhat egy megbízható hitelesítésszolgáltató tanúsítványával, amelynek tárgya megegyezik a jegyzékfájllal Publisher.

A jegyzékfájl logói futásidőben a külső helyről töltődnek be, nem a csak identitást tartalmazó .msix elemből. A regisztráció előtt másolja a létrehozott erőforrásokat az exe-fájl mellé (a külső helyre) – ellenkező esetben a Windows olyan elrendezést regisztrál, amelyből hiányzik az összes embléma, amelyre a jegyzékfájl hivatkozik:

# Copy the generated assets into the external location (beside your exe)
Copy-Item ./sparse/Assets -Destination .\bin\Release\net8.0-windows\Assets -Recurse -Force

Regisztrálja az identitáscsomagot a mappában (a külső helyen):

Add-AppxPackage -Path .\MyApp.identity.msix `
  -ExternalLocation (Resolve-Path .\bin\Release\net8.0-windows)

Indítsa el az alkalmazást, és győződjön meg arról, hogy az identitás jelen van. Például Windows.ApplicationModel.Package.Current.Id.FamilyName a csomagcsalád nevét adja vissza a dobás helyett.

A csomag regisztrációjának törlése

A regisztráció törlése a munkafolyamat 4. lépésének is része. Távolítsa el a regisztrációt, amikor befejezi a helyi tesztelést, vagy amikor a telepítő eltávolítja az alkalmazást.

Keresse meg a csomagot a következővel: Get-AppxPackage, majd irányítsa a kimenetét a következőre: Remove-AppxPackage:

Get-AppxPackage -Name MyApp | Remove-AppxPackage

Cserélje le a(z) MyApp elemet a csomag nevére. Ha nem biztos a pontos névben, először a(z) Get-AppxPackage -Name *MyApp* egyezéseit sorolja fel.

Regisztráció integrálása a telepítőbe

A regisztráció és a regisztráció törlése a telepítő feladata, és a minta megegyezik a telepítő eszközei között:

  • Telepítés: másolja az alkalmazás bináris fájljait, a Assets/ mappát és a .msix telepítési könyvtárba, majd futtassa Add-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>".
  • Eltávolítás: a fájlok törlése előtt futtassa Remove-AppxPackage <full-package-name> .

Mivel az erőforrások külső helyről töltődnek be, a Assets/ mappát mindig az alkalmazás mellett, a manifest által elvárt elrendezésben telepítse.

Warning

A telepítési könyvtár a telepítéskor kerül meghatározásra, és tartalmazhat olyan karaktereket is (például szimpla idézőjelet), amelyek megszakítanak egy PowerShell-karakterlánc-literált. Mindig escape-elje vagy ellenőrizze az elérési utat, mielőtt interpolálná egy -Command karakterláncba. Részesítse előnyben az elérési utak argumentumként való átadását egy -File szkript számára a soron belüli -Command interpoláció helyett. Az alábbi WiX- és NSIS-kódrészletek megbízható telepítési útvonalat feltételeznek, míg az Inno telepítőpéldája a biztonságos menekülést mutatja be.

Inno Setup

Állítsa össze a PowerShell-argumentumokat egy [Code] függvényben úgy, hogy a futtatókörnyezet telepítési elérési útja megfelelően escape-elve legyen az egyszeres idézőjellel határolt PowerShell-literálhoz (az olyan telepítési könyvtár, amely ' karaktert tartalmaz, nem teheti lehetővé szkript befecskendezését):

[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;

Egy teljes, működő setup.iss példáért tekintse meg a sparse-app mintát.

Regisztrációs szkript WiX-hez és NSIS-hez

A WiX- és NSIS-példák egy kis register-sparse.ps1 segédet hívnak meg -File használatával, így a telepítési útvonalat paraméterként adják át (a PowerShell adatként kezeli), nem pedig egy -Command karakterláncba interpolálják. Így elkerülhető a szkriptinjektálás egy létrehozott telepítési könyvtáron keresztül (például egy idézőjelet tartalmazó mappanév vagy $(...)):

# 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)

Regisztráljon felhasználónként (Impersonate="yes"), mert Add-AppxPackage regisztrálja a csomagot az azt futtató fiókhoz. A Impersonate="no" használatával végrehajtott halasztott művelet LocalSystem néven fut, ami nem biztosítja a telepítést végző felhasználó identitását (és ezt általában elutasítják). Gépszintű MSI esetén a regisztrációt megszemélyesítve futtassa, hogy az a regisztrációt kezdeményező felhasználóra vonatkozzon.

A halasztott egyéni művelet nem tudja közvetlenül olvasni a(z) INSTALLFOLDER elemet (a halasztott műveletek olyan környezetben futnak, ahol nincs hozzáférésük a tulajdonságokhoz), és a művelet puszta deklarálása nem indítja el azt. Vezesse át az elérési utakat a(z) CustomActionData elemen keresztül — egy azonnali, 51-es típusú műveleten, amelynek Property neve megegyezik a halasztott művelet Id nevével —, és ütemezze mindkettőt a(z) InstallFiles után:

<!-- 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 &quot;[INSTALLFOLDER]register-sparse.ps1&quot; -MsixPath &quot;[INSTALLFOLDER]MyApp.identity.msix&quot; -ExternalLocation &quot;[INSTALLFOLDER]&quot; -PackageName &quot;MyPackageIdentityName&quot;" />

<!-- 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 a WiX util bővítmény részeként érhető el (WixUtilExtension); hivatkozzon erre, hogy a WixCA bináris elérhető legyen.

Megjegyzés:

Egyetlen megszemélyesített művelet csak a telepítőt futtató felhasználó identitását regisztrálja. Ha egy gépenkénti telepítés minden felhasználója számára szeretné biztosítani az üzembe helyezést, regisztráljon helyette az első indításkor (felhasználónként), vagy használjon üzembehelyezési mechanizmust, például 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

Gyakori problémák elhárítása

A Package.Current futásidőben nem ad ki vagy jelent identitást

  • Az identitáscsomag nincs regisztrálva, vagy az exe fúziós jegyzékfájlja hiányzik az <msix> elemből. Futtassa újra a(z) winapp embed-identity elemet (és építse újra, ha XML módot használ), majd regisztrálja újra a(z) Add-AppxPackage -ExternalLocation használatával.
  • A <msix packageName>, publisherés applicationId az exe fájlnak pontosan meg kell egyeznie a regisztrált csomag identitásával.

Az eszközök vagy emblémák nem jelennek meg

  • Győződjön meg arról, hogy a Assets/ mappa a külső helyre van telepítve, ugyanazokkal a relatív elérési utakkal, amelyeket a manifesztum elvár. Az eszközök a külső helyről vannak feloldva, nem a .msix.

Add-AppxPackage aláírási vagy megbízhatósági hibával meghiúsul

  • A .msix tanúsítványt olyan tanúsítvánnyal kell aláírni, amely megbízható a gépen, és amelynek tárgya megegyezik a jegyzékfájléval Publisher. Helyi teszteléshez hozzon létre egy fejlesztési tanúsítványt a winapp cert generate használatával, jelölje megbízhatónak, és győződjön meg arról, hogy a Publisher jegyzékfájl megfelel ennek a tanúsítványnak.

A MakeAppx azt jelzi, hogy a win32App nem deklarálhat EntryPointot

  • Egy win32App ritkás alkalmazás nem deklarálhat EntryPoint. A winapp init --sparse által létrehozott jegyzékfájlok már helyesek; távolítsa el a EntryPoint attribútumot, ha kézzel szerkesztette a jegyzékfájlt.

"A bemenet fájl, de nem ritka jegyzék"

  • winapp pack <file> csak olyan jegyzéket fogad el, amely deklarálja a(z) <uap10:AllowExternalContent>true</uap10:AllowExternalContent> elemet. Hozzon létre egyet a winapp init --exe <exe> --sparse használatával, vagy adjon meg egy bemeneti mappát egy teljes MSIX létrehozásához.