Seyrek paketleme: Paketlenmemiş bir uygulamaya kimlik verme

Çalışan bir uçtan uca örnek (WPF uygulama + Inno Kurulum yükleyicisi) için seyrek uygulama örneğine bakın.

, MSBuild, CMake veya başka bir araç zinciriyle dotnet buildoluşturulan standart masaüstü yürütülebilir dosyasının paket kimliği yoktur. Kimlik olmadan birçok modern Windows API'sini (bildirim bildirimleri, arka plan görevleri, paylaşım hedefleri, başlangıç görevleri, uygulama veri API'leri ve daha fazlası) kullanamaz.

Dağınık paketleme, ikili dosyaları MSIX'e taşımadan uygulamaya kimlik kazandırır. Yalnızca kimlik içeren.msix küçük bir paket (yalnızca bir bildirim dosyası) sunar ve bunu harici bir konum kullanarak normal şekilde yüklü uygulamanızla birlikte kaydedersiniz. Cihazınız .exe tam olarak yükleyicinizin yerleştirdiği yerde kalır. Bu, yalnızca geliştirme sırasında hata ayıklama için kullanılan winapp create-debug-identity öğesinin üretim ortamındaki karşılığıdır.

Bu kılavuz, paketlenmemiş uygulamalara kimlik verme iş akışının ilk üç adımına eşlenen üç CLI adımını kapsar:

Aşama Komut Result
1. Kimlik bildirimini oluşturma winapp init --exe <exe> --sparse sparse/appxmanifest.xml + sparse/Assets/
2. Kimlik paketini oluşturma ve imzalama winapp pack <appxmanifest.xml> --cert <pfx> <PackageName>.identity.msix
3. Uygulamaya kimlik ekleme winapp embed-identity <exe> <msix> exe'nin fusion bildirimindeki öğesi

Belgelerden 4-5 arası adımlar (paketi kaydetme/kaydını kaldırma) yükleyicinizin sorumluluğundadır; bkz. Yükleyici tümleştirmesi.

Seyrek paketleme ne zaman kullanılır?

  • Zaten olgun bir yükleyiciniz (Inno Kurulumu, WiX, NSIS, MSI) var ve dağıtım için MSIX'e geçmek istemiyorsunuz, ancak kimlik geçitli Windows API'lerine ihtiyacınız var.
  • Uygulamanızın, MSIX’in izin vermediği bir konuma veya yerleşim düzenine yüklenmesi gerekir.
  • Çok az ve ek bir değişiklik istiyorsunuz: Mevcut yükleme akışınızı koruyun ve bir .msix kayıt adımı ekleyin.

Yeni başlıyorsanız ve MSIX olarak dağıtabiliyorsanız, tam paketlenmiş bir uygulama (winapp init + winapp pack <folder>) daha basittir.

Prerequisites

  1. Windows 10, sürüm 2004 (derleme 19041) veya üzeri. Sparse paketler uap10:AllowExternalContent kullanır ve bu da 19041+ gerektirir.
  2. winapp CLI — winget aracılığıyla yükleyin (veya zaten yüklüyse güncelleştirin):
    winget install Microsoft.WinApp --source winget
    
  3. Hedef makinede güvenilen bir kod imzalama sertifikası. Yerel test için, ile winapp cert generate bir geliştirme sertifikası oluşturun ve ona güvenin. Üretim paketleri, konusu bildirimiyle Publishereşleşen bir sertifikayla imzalanmalıdır.

Walkthrough

Aşağıdaki örneklerde adresinde ./bin/Release/net8.0-windows/MyApp.exederlenmiş bir yürütülebilir dosya olduğu varsayılır.

1. Adım — Seyrek kimlik bildirimi oluşturma

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

Bu, exe dosyasından paket adını, yayımcısını, sürümünü ve açıklamasını (dosya sürümü bilgileri aracılığıyla) çıkarsar ve bunları kabul edip geçersiz kılmanızı ister. CI’da istemleri atlamak için / (veya --name) ve belirli değerleri geçersiz kılmak için --publisher--no-prompt--use-defaults ekleyin:

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

Varsayılan olarak aşağıdakileri geçerli dizindeki özel sparse/ klasörüne yazar (--output-dir ile geçersiz kılabilirsiniz):

  • appxmanifest.xml<uap10:AllowExternalContent>true</uap10:AllowExternalContent> içeren (<Properties> altındaki bir öğe), ProcessorArchitecture="neutral", bir win32App uygulaması ve Executable içine doldurulmuş exe adı bulunan minimal bir manifest.
  • Assets/ — yer tutucu görsel varlıkları (mümkün olduğunda exe'nin simgesinden ayıklanır).

Neden exe'nin yanında olmayan bir sparse/ klasör? Bildirim ve Assets/, winapp embed-identity ile tarafından kullanılan winapp pack — çalışma zamanında bunları exe’nin yanından okuyup kullanan hiçbir şey yoktur (çalışma zamanı kimliği, exe içine gömülü <msix> öğesinden ve kayıtlı paketin dış konumundan gelir; ayrıca bildirim, exe’ye ad ile başvurur, bu nedenle konumu exe’nin bulunduğu yerden bağımsızdır). Bunları özel, sürüm denetimine alınmış bir klasöre yazmak, onları temiz/yeniden derleme işleminin sileceği bir derleme çıktı dizininin (örneğin bin/) dışında tutar ve sonraki adımların temiz kalması için klasörü ikili dosyalardan arındırır. winapp pack ve winapp embed-identity, otomatik olarak sparse/ içinde arar; bu nedenle yolu belirtmeniz nadiren gerekir.

Not: Seyrek başlatma akışı tüm SDK/paket yüklemesini kasıtlı olarak atlar ; yalnızca kimlik paketleri SDK bağımlılıklarına sahip değildir.

Hedef dizinde zaten bir appxmanifest.xml varsa, init onu (ve ona ait Assets/ öğesini) ezmek yerine durur. Yeniden oluşturmak için --force ile yeniden çalıştırın.

Oluşturulan manifestteki Publisher öğesinin, imzalamada kullanacağınız sertifikayla eşleştiğinden emin olun. Gerekirse appxmanifest.xml öğesini düzenleyin veya oluştururken --publisher öğesini iletin.

2. Adım — Kimlik paketini oluşturma ve imzalama

winapp pack öğesini seyrek manifeste yöneltin (bir dosya, klasör değil):

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

Manifest AllowExternalContent tanımladığı için, winapp pack yalnızca manifest içeren bir yalnızca kimlik.msix oluşturur — ikili yok, varlık yok. Çıkış varsayılan olarak <PackageName>.identity.msix geçerli dizinde olur; bunu değiştirmek için kullanın --output . İmzalama, yalnızca --generate-cert'ı (veya --cert'i) sağladığınızda gerçekleşir.

3. Adım — Uygulamanıza kimlik ekleme

Windows'un çalışan .exe'yi kimlik paketine bağlaması için <msix> öğesini ekleyin:

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

Ya da yan yana bildirim dosyasını sürüm denetimine eklenmiş bir dosya olarak koruyup yeniden derleyin:

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

XML modunda <msix> , öğe hedef bildirime eklenir (veya içinde değiştirilir). Bu manifest dosyasına projenizden başvuru ekleyin (.NET için <ApplicationManifest>app.manifest</ApplicationManifest> ayarlayın) ve öğenin exe içine gömülmesi için yeniden derleyin.

Her iki mod da kimlik bilgisini seyrek bir appxmanifest.xml öğesinden okur. --manifest öğesini atlarsanız, winapp önce hedefin yanındaki bir winapp init --exe --sparse klasörüne (buraya sparse/ varsayılan olarak yazar), ardından geçerli dizine bakar; bunlarda bulamazsa hedefin yanındaki konuma ve geçerli dizine geri düşer; başka bir konum belirtmek için --manifest parametresini kullanın.

Not: EXE modu, ikili dosyayı mt.exe ile yeniden yazar; bu da mevcut tüm Authenticode imzalarını geçersiz kılar. Dağıtmadan önce exe'yi (örneğin winapp sign ./MyApp.exe <cert.pfx>) yeniden imzalayın.

4. Adım — Kaydolma (yerel test için)

Bildirimin logoları çalışma zamanında dış konumdan çözümlenir, yalnızca .msixkimlikten çözümlenmez. 1. Adım bunları ./sparse/Assets altına yazdı; bu yüzden, kayıt etmeden önce bunları .exe dosyanızın yanına (harici konuma) kopyalayın — aksi takdirde Windows, manifestin başvurduğu tüm logoları eksik olan bir düzeni kaydeder:

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

Ardından kimlik paketini bu klasöre ( dış konum) kaydedin:

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

Uygulamayı başlatın ve kimliğin mevcut olduğunu onaylayın; örneğin, Windows.ApplicationModel.Package.Current.Id.FamilyName oluşturma yerine paket aile adınızı döndürmelidir.

Temizlemek için:

Remove-AppxPackage <full-package-name>

Varlık işleme

Seyrek .msix yalnızca kimliktir. Bildirim dosyasının başvurduğu görsel varlıklar (Assets\StoreLogo.png, kutucuklar vb.), çalışma zamanında dış içerik konumundan — yani uygulamanızın yükleme dizininden — alınır; içinden .msix.

Bu, Assets/ klasörünü uygulamanızla birlikte dağıtmanız gerektiği anlamına gelir (manifestin beklediği aynı düzenle, dış konuma göre).

2. adım, bildirim dosyasını doğrudan (winapp pack ./sparse/appxmanifest.xml ) paketler. Bu dosya yalnızca bu bildirimden yalnızca .msix kimliği oluşturur; eşdüzey dosyalar yoksayılır, bu nedenle varlıklarınızı veya ikili dosyalarınızı hiçbir zaman içermez. (Bunun yerine AllowExternalContentbildiriminde winapp pack beyan eden bir klasörü belirtirseniz, bulduğu varlıklar ya da ikili dosyalar hakkında uyarır; çünkü seyrek bir pakette bunların yeri .msix içinde değil, dış konumdur.)

Yükleyici tümleştirmesi

Kayıt ve kayıt silme, yükleyicinin işidir. Desen, yükleyici araçları arasında aynıdır:

  • Yükleme: Uygulama ikili dosyalarınızı, Assets/ klasörünü ve öğesini .msix yükleme dizinine kopyalayın, ardından komutunu çalıştırın Add-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>".
  • Kaldır: Dosyaları silmeden önce komutunu çalıştırın Remove-AppxPackage <full-package-name> .

Güvenlik: Yükleme dizini yükleme zamanında çözülür ve PowerShell dize değişmez değerinin dışına çıkan karakterler (tek tırnak gibi) içerebilir. Yolu, bir -Command dizesine eklemeden önce her zaman uygun şekilde kaçırın veya doğrulayın — aşağıdaki WiX ve NSIS kod parçacıkları güvenilir bir yükleme yolunun kullanıldığını varsayarken, Inno Setup örneği güvenli kaçışlamayı gösterir. Yolları satır içi -File interpolasyon yerine bir -Command betiğine argüman olarak geçirmeyi tercih edin.

Inno Kurulumu

PowerShell bağımsız değişkenlerini bir [Code] işlevinde oluşturun; böylece çalışma zamanı kurulum yolu, tek tırnaklı PowerShell sabiti için uygun şekilde kaçışlanır (içinde ' bulunan bir kurulum dizini betik enjekte edememelidir):

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

Eksiksiz, çalışan bir için setup.iss örneğine bakın.

Aşağıdaki WiX ve NSIS örnekleri, register-sparse.ps1 aracılığıyla küçük bir -File çağırır; böylece yükleme yolu, bir -Command dizesinin içine gömülmek yerine parametre olarak iletilir (PowerShell bunu veri olarak bağlar). Bu, hazırlanmış bir yükleme dizini (örneğin, tırnak işareti veya $(...)içeren bir klasör adı) aracılığıyla betik eklenmesini önler:

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

Kullanıcı başına kaydedin (Impersonate="yes"), çünkü Add-AppxPackage paketi onu çalıştıran hesap için kaydeder. LocalSystem içeren ertelenmiş bir eylem, olarak çalıştırılır; bu da yükleyen kullanıcıya kimlik Impersonate="no" (ve genellikle reddedilir). Makine başına yüklenen bir MSI için, kayıt işlemini çağıran kullanıcının kimliğiyle çalıştırın; böylece bu işlem çağıran kullanıcıya uygulanır.

Ertelenmiş bir özel eylem, INSTALLFOLDER öğesini doğrudan okuyamaz (çünkü ertelenmiş eylemler özelliklere erişimi olmayan bir bağlamda çalışır) ve eylemi yalnızca tanımlamak onu çalıştırmaz. Öyleyse, yolları CustomActionData üzerinden geçirin — Property adı ertelenen eylemin Id ile aynı olan anında bir type-51 eylemi — ve InstallFiles sonrasına her ikisini de zamanlayı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, WiX yardımcı program uzantısıyla birlikte gelir (WixUtilExtension); WixCA ikilisinin kullanılabilir olması için buna başvuru ekleyin.

Kimliğine bürünülen tek bir eylem yalnızca yükleyiciyi çalıştıran kullanıcı için kimlik kaydeder. Makine başına yapılan bir yüklemenin tüm kullanıcılarını hazırlamak için, bunun yerine ilk çalıştırmada kullanıcı başına kaydedin veya Add-AppxProvisionedPackage gibi bir hazırlama mekanizması kullanın.

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

Sorun giderme

Package.Current çalışma zamanında / "paket kimliği yok" hatası veriyor

  • Kimlik paketi kayıtlı değil veya exe'nin fusion bildiriminde <msix> öğesi eksik. winapp embed-identity öğesini yeniden çalıştırın (XML modu kullanıyorsanız yeniden derleyin), ardından Add-AppxPackage -ExternalLocation ile yeniden kaydedin.
  • <msix packageName> / applicationId / publisherexe dosyasındaki, kayıtlı paketin kimliğiyle tam olarak eşleşmelidir.

Varlıklar/logolar görünmüyor

  • Assets/ klasörünün, manifestin beklediği aynı göreli yollarla harici konuma yerleştirildiğinden emin olun. Varlıklar, .msix yerine dış konumdan çözülür.

Add-AppxPackage imzalama / güven hatasıyla başarısız oluyor

  • , .msix makinede güvenilen ve konusu bildirimiyle Publishereşleşen bir sertifika tarafından imzalanmalıdır. Yerel testler için, winapp cert generate ile bir geliştirme sertifikası oluşturup buna güvenin ve manifest dosyasının Publisher bununla eşleştiğinden emin olun.

MakeAppx: "RuntimeBehavior değeri 'win32App' olan uygulama EntryPoint bildirmemelidir"

  • win32App sparse bir uygulama, EntryPoint bildirmemelidir. tarafından winapp init --sparse oluşturulan bildirimler zaten doğru; bildirimi el ile düzenlediyseniz tüm EntryPoint öznitelikleri kaldırın.

"Giriş bir dosya ama seyrek bildirim değil"

  • winapp pack <file>, yalnızca <uap10:AllowExternalContent>true</uap10:AllowExternalContent> bildiren bir manifesti kabul eder. winapp init --exe <exe> --sparse ile bir tane oluşturun veya tam bir MSIX oluşturmak için bir girdi klasörü belirtin.

Ayrıca bakınız