关于 dotnet-msix
dotnet-msix 是面向 .NET 桌面应用开发者的 MSIX 打包技能,涵盖创建、签名、分发、更新和 CI 构建,并说明单项目打包与 WAP 项目的做法。
主要功能
- 01从 csproj 或 WAP 创建 MSIX
- 02配置证书签名
- 03准备 Store 和旁加载分发
- 04设置 App Installer 更新
- 05构建多架构捆绑包
- 06将 MSIX 步骤加入 CI/CD
适用场景
- 01为 .NET 桌面应用创建发布包
- 02为部署配置证书签名
- 03设置带自动更新的直接分发
- 04构建多架构应用包
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-msix安装后在 Claude Code 中输入/dotnet-msix确认已加载。
详细介绍
dotnet-msix
面向 .NET 桌面应用程序的 MSIX 打包流水线:从 csproj(WindowsPackageType)和 WAP 项目创建程序包、证书签名(开发用自签名证书、生产用可信 CA 证书、Microsoft Store 签名)、分发渠道(提交到 Microsoft Store、通过 App Installer 旁加载、通过 SCCM/Intune 进行企业部署)、自动更新配置(App Installer XML、版本检查、差异更新)、用于多架构的 MSIX 捆绑包格式(.msixbundle),以及 CI/CD MSIX 构建步骤。
**版本假设:**Windows App SDK 1.6+(当前稳定版)。使用 Windows App SDK 的 MSIX 最低要求为 Windows 10 build 19041+。App Installer 自动更新协议要求 Windows 10 build 1709+。基线为 .NET 8.0+。
**范围边界:**本技能负责 MSIX 打包流水线——创建、签名、分发、提交到 Store、自动更新和 CI 构建步骤。WinUI 3 项目设置、XAML 模式,以及打包与未打包部署模式的对比由 [skill:dotnet-winui] 负责。通用 Native AOT 编译(PublishAot、ILLink 描述符)由 [skill:dotnet-native-aot] 负责。通用 CI/CD 流水线模式由 [skill:dotnet-gha-patterns] 和 [skill:dotnet-ado-patterns] 负责。
**不在范围内:**WinUI 3 项目设置和 MSIX 与未打包模式的对比——参见 [skill:dotnet-winui]。Native AOT MSBuild 配置——参见 [skill:dotnet-native-aot]。通用 CI/CD 流水线模式——参见 [skill:dotnet-gha-patterns] 和 [skill:dotnet-ado-patterns]。通用 NuGet 打包——参见 [skill:dotnet-nuget-authoring]。基于容器的部署——参见 [skill:dotnet-containers]。
交叉引用:[skill:dotnet-winui](WinUI 项目设置和打包模式对比)、[skill:dotnet-native-aot](AOT + MSIX 场景)、[skill:dotnet-gha-patterns](CI 流水线结构)、[skill:dotnet-ado-patterns](ADO 流水线结构)、[skill:dotnet-nuget-authoring](NuGet 打包)。
MSIX 程序包创建
从 csproj 创建(单项目打包)
现代 WinUI 3 和 Windows App SDK 应用可以直接从应用 .csproj 生成 MSIX 程序包,无需单独的 Windows Application Packaging(WAP)项目。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<UseWinUI>true</UseWinUI>
<WindowsPackageType>MSIX</WindowsPackageType>
<AppxPackageDir>$(SolutionDir)AppPackages\</AppxPackageDir>
<GenerateAppxPackageOnBuild>false</GenerateAppxPackageOnBuild>
</PropertyGroup>
</Project>
构建 MSIX 程序包:
# Build MSIX package
dotnet publish --configuration Release --runtime win-x64 \
/p:GenerateAppxPackageOnBuild=true \
/p:AppxPackageSigningEnabled=false
# Output: AppPackages\MyApp_1.0.0.0_x64.msix
WAP 项目(Desktop Bridge)
对于非 WinUI 桌面应用(WPF、WinForms),请使用 Windows Application Packaging Project 将现有应用封装为 MSIX。WAP 项目(.wapproj)通过 Visual Studio 的“Windows Application Packaging Project”模板创建——它们使用专用的项目格式,而非标准的 Microsoft.NET.Sdk。
关键配置是引用桌面应用项目:
<ItemGroup>
<ProjectReference Include="..\MyWpfApp\MyWpfApp.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<DefaultLanguage>en-US</DefaultLanguage>
<AppxPackageDir>$(SolutionDir)AppPackages\</AppxPackageDir>
</PropertyGroup>
Package.appxmanifest
清单定义标识、功能和视觉资源:
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="MyCompany.MyApp"
Publisher="CN=My Company, O=My Company, L=Seattle, S=WA, C=US"
Version="1.0.0.0"
ProcessorArchitecture="x64" />
<Properties>
<DisplayName>My App</DisplayName>
<PublisherDisplayName>My Company</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop"
MinVersion="10.0.19041.0"
MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>
<Resource Language="en-us" />
</Resources>
<Applications>
<Application Id="App"
Executable="MyApp.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements DisplayName="My App"
Description="My application description"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
使用证书签名
所有 MSIX 程序包都必须经过签名才能在 Windows 上安装。签名证书的 Subject 必须与程序包清单中的 Publisher 属性匹配。
自签名证书(开发)
# Create a self-signed certificate for development
$cert = New-SelfSignedCertificate `
-Type Custom `
-Subject "CN=My Company, O=My Company, L=Seattle, S=WA, C=US" `
-KeyUsage DigitalSignature `
-FriendlyName "MyApp Dev Signing" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", `
"2.5.29.19={text}")
# Export PFX for CI usage
$password = ConvertTo-SecureString -String "$env:CERT_PASSWORD" -Force -AsPlainText
Export-PfxCertificate -Cert $cert `
-FilePath "MyApp_DevSigning.pfx" `
-Password $password
# Find signtool.exe dynamically (SDK version varies by machine)
$signtool = Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" |
Sort-Object { [version]($_.Directory.Parent.Name) } -Descending |
Select-Object -First 1 -ExpandProperty FullName
# Sign the MSIX package
& $signtool sign /fd SHA256 /a /f "MyApp_DevSigning.pfx" `
/p "$env:CERT_PASSWORD" `
"AppPackages\MyApp_1.0.0.0_x64.msix"
可信 CA 证书(生产)
对于 Microsoft Store 之外的生产分发:
- 获取代码签名证书,由可信 CA(DigiCert、Sectigo、GlobalSign)颁发
- Subject 必须与
Package.appxmanifest中的Publisher匹配 - 为签名添加时间戳,以确保长期有效
# Sign with a trusted CA certificate (from certificate store)
& signtool.exe sign /fd SHA256 /sha1 "THUMBPRINT_HERE" `
/tr http://timestamp.digicert.com /td SHA256 `
"AppPackages\MyApp_1.0.0.0_x64.msix"
# Sign with a PFX file
& signtool.exe sign /fd SHA256 /f "production-cert.pfx" `
/p "$env:CERT_PASSWORD" `
/tr http://timestamp.digicert.com /td SHA256 `
"AppPackages\MyApp_1.0.0.0_x64.msix"
Microsoft Store 签名
提交到 Microsoft Store 的应用会在接收处理过程中由 Microsoft 重新签名。开发签名证书会被替换为 Microsoft 颁发的证书。仅通过 Store 分发时,无需生产签名证书。
<PropertyGroup>
<AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>
<PackageCertificateThumbprint>AUTO_GENERATED_BY_VS</PackageCertificateThumbprint>
</PropertyGroup>
证书要求摘要
| 分发方式 | 证书类型 | 是否需要受信任的 CA |
|---|---|---|
| 开发/测试 | 自签名 | 否(手动安装证书) |
| 企业旁加载 | 自签名或内部 CA | 否(通过组策略部署证书) |
| 直接下载 | 受信任的 CA(DigiCert 等) | 是 |
| Microsoft Store | 测试证书(由 MS 重新签名) | 否 |
分发渠道
提交至 Microsoft Store
- 在 Partner Center 注册为 Windows 开发者(https://partner.microsoft.com/dashboard)
- 创建应用预留以确保应用名称
- 构建 MSIX 包并关联 Store:
# Build for Store submission (creates .msixupload)
dotnet publish --configuration Release --runtime win-x64 \
/p:GenerateAppxPackageOnBuild=true \
/p:UapAppxPackageBuildMode=StoreUpload \
/p:AppxBundle=Auto
- 通过 Partner Center 提交
.msixupload文件 - Microsoft 验证、签名并发布应用
App Installer 旁加载
App Installer(.appinstaller)支持直接分发和自动更新。将文件托管在 Web 服务、网络共享或 CDN 上。
<?xml version="1.0" encoding="utf-8"?>
<AppInstaller Uri="https://mycompany.com/apps/MyApp.appinstaller"
Version="1.0.0.0"
xmlns="http://schemas.microsoft.com/appx/appinstaller/2021">
<MainPackage Name="MyCompany.MyApp"
Version="1.0.0.0"
Publisher="CN=My Company, O=My Company, L=Seattle, S=WA, C=US"
ProcessorArchitecture="x64"
Uri="https://mycompany.com/apps/MyApp_1.0.0.0_x64.msix" />
<UpdateSettings>
<OnLaunch HoursBetweenUpdateChecks="12"
ShowPrompt="true"
UpdateBlocksActivation="false" />
<AutomaticBackgroundTask />
<ForceUpdateFromAnyVersion>false</ForceUpdateFromAnyVersion>
</UpdateSettings>
</AppInstaller>
用户通过以下方式安装:
ms-appinstaller:?source=https://mycompany.com/apps/MyApp.appinstaller
企业部署
对于受管理的企业环境:
| 方式 | 工具 | 最适合 |
|---|---|---|
| Microsoft Intune | Intune 门户 | 云管理设备 |
| SCCM/MECM | Configuration Manager | 本地部署管理的设备 |
| Group Policy | DISM / PowerShell | 已加入域的设备 |
| PowerShell | Add-AppxPackage | 基于脚本的部署 |
# Enterprise deployment via PowerShell
Add-AppxPackage -Path "\\fileserver\apps\MyApp_1.0.0.0_x64.msix"
# Install for all users (requires admin)
Add-AppxProvisionedPackage -Online `
-PackagePath "MyApp_1.0.0.0_x64.msix" `
-SkipLicense
自动更新配置
App Installer 自动更新(Windows 10 1709+)
App Installer XML 文件控制自动更新行为:
<UpdateSettings>
<OnLaunch HoursBetweenUpdateChecks="12"
ShowPrompt="true"
UpdateBlocksActivation="false" />
<AutomaticBackgroundTask />
<ForceUpdateFromAnyVersion>false</ForceUpdateFromAnyVersion>
</UpdateSettings>
| 设置 | 描述 |
|---|---|
HoursBetweenUpdateChecks | 两次更新检查之间的最短间隔小时数(0 = 每次启动时检查) |
ShowPrompt | 更新前向用户显示更新对话框 |
UpdateBlocksActivation | 更新完成前阻止应用启动 |
AutomaticBackgroundTask | 在后台检查更新,无需启动应用 |
ForceUpdateFromAnyVersion | 允许从任意版本更新(包括降级) |
通过代码检查更新(Windows App SDK)
对于需要自定义更新 UI 或逻辑的应用,请使用 Package.Current.CheckUpdateAvailabilityAsync():
using Windows.ApplicationModel;
public class AppUpdateService
{
public async Task<bool> CheckForUpdatesAsync()
{
var result = await Package.Current.CheckUpdateAvailabilityAsync();
return result.Availability == PackageUpdateAvailability.Available
|| result.Availability == PackageUpdateAvailability.Required;
}
}
差分更新
MSIX 支持差分更新——只下载有更改的块。满足以下条件时会自动启用:
- 使用相同的
PackageName和Publisher - 递增
Version - 新旧两个包都托管在同一台服务上
差分更新无需额外配置。
MSIX Bundle 格式
MSIX Bundle(.msixbundle)将多个特定于架构的 MSIX 包打包为单个可下载文件。Windows 会自动安装适合的架构。
创建 Bundle
# Build for multiple architectures
dotnet publish -c Release -r win-x64 /p:GenerateAppxPackageOnBuild=true
dotnet publish -c Release -r win-arm64 /p:GenerateAppxPackageOnBuild=true
# Find MakeAppx.exe dynamically (SDK version varies by machine)
$makeappx = Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\MakeAppx.exe" |
Sort-Object { [version]($_.Directory.Parent.Name) } -Descending |
Select-Object -First 1 -ExpandProperty FullName
# Create bundle
& $makeappx bundle /d "AppPackages" /p "MyApp_1.0.0.0.msixbundle"
MSBuild Bundle 生成
<PropertyGroup>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x64|arm64</AppxBundlePlatforms>
</PropertyGroup>
Bundle 布局
MyApp_1.0.0.0.msixbundle
MyApp_1.0.0.0_x64.msix
MyApp_1.0.0.0_arm64.msix
CI/CD MSIX 构建步骤
GitHub Actions MSIX 构建
# MSIX-specific build steps (embed in your CI workflow)
# For pipeline structure, see [skill:dotnet-gha-patterns]
jobs:
build-msix:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Decode signing certificate
run: |
$pfxBytes = [System.Convert]::FromBase64String("${{ secrets.SIGNING_CERT_BASE64 }}")
[System.IO.File]::WriteAllBytes("signing-cert.pfx", $pfxBytes)
shell: pwsh
- name: Build MSIX package
run: |
dotnet publish --configuration Release --runtime win-x64 `
/p:GenerateAppxPackageOnBuild=true `
/p:AppxPackageSigningEnabled=true `
/p:PackageCertificateKeyFile="${{ github.workspace }}\signing-cert.pfx" `
/p:PackageCertificatePassword="${{ secrets.CERT_PASSWORD }}"
shell: pwsh
- name: Upload MSIX artifact
uses: actions/upload-artifact@v4
with:
name: msix-package
path: AppPackages/**/*.msix
- name: Clean up certificate
if: always()
run: Remove-Item -Path "signing-cert.pfx" -ErrorAction SilentlyContinue
shell: pwsh
Azure DevOps MSIX 构建
# MSIX-specific build steps (embed in your ADO pipeline)
# For pipeline structure, see [skill:dotnet-ado-patterns]
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DownloadSecureFile@1
name: signingCert
inputs:
secureFile: 'signing-cert.pfx'
- task: DotNetCoreCLI@2
displayName: 'Build MSIX'
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/MyApp.csproj'
arguments: >-
--configuration Release
--runtime win-x64
/p:GenerateAppxPackageOnBuild=true
/p:AppxPackageSigningEnabled=true
/p:PackageCertificateKeyFile=$(signingCert.secureFilePath)
/p:PackageCertificatePassword=$(CERT_PASSWORD)
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: 'AppPackages'
artifactName: 'msix-package'
AOT + MSIX
MSIX 包可以包含 AOT 编译的二进制文件,以加快启动速度并缩小运行时占用。将 PublishAot 与 MSIX 打包结合使用:
<PropertyGroup>
<PublishAot>true</PublishAot>
<WindowsPackageType>MSIX</WindowsPackageType>
</PropertyGroup>
有关 AOT MSBuild 配置详情(ILLink 描述符、修剪选项、平台注意事项),请参阅 [skill:dotnet-native-aot]。
Windows SDK 版本要求
| 功能 | 最低 Windows 版本 | Build 号 |
|---|---|---|
| 搭配 Windows App SDK 的 MSIX | Windows 10 | Build 19041 (2004) |
| App Installer 协议 | Windows 10 | Build 1709 (Fall Creators Update) |
| 自动更新(OnLaunch) | Windows 10 | Build 1709 |
| 后台自动更新 | Windows 10 | Build 1803 (April 2018 Update) |
| ForceUpdateFromAnyVersion | Windows 10 | Build 1809 |
| MSIX Bundle 格式 | Windows 10 | Build 1709 |
| 可选包 | Windows 10 | Build 1709 |
| 修改包 | Windows 10 | Build 1809 |
| App Installer 文件托管 | Windows 10 | Build 1709 |
| Microsoft Store 提交 | Windows App SDK 1.6+ | N/A |
目标平台版本配置
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
</PropertyGroup>
Agent 注意事项
-
清单中的
Publisher必须与签名证书的 Subject 完全一致——不匹配会在构建或签名时导致SignTool Error: SignerSign() failed。 -
自签名证书需要手动安装信任——用户必须先将证书安装到
Trusted People或Trusted Root Certification Authorities,才能安装 MSIX。 -
切勿将 PFX 文件或证书密码提交到源代码管理中——请将证书存储为 CI 机密(GitHub Secrets、Azure DevOps Secure Files),并在构建管道中解码。
-
只有在构建多个体系结构时,
AppxBundle=Auto才会生成捆绑包——对于单体系结构构建,它会生成普通的.msix文件,而不是捆绑包。 -
MSIX 应用运行在类似容器的沙盒中——文件系统访问会被虚拟化。写入
AppData的应用会被重定向到软件包专属位置。请使用ApplicationData.CurrentAPI,而不要使用硬编码路径。 -
向 Store 提交时使用
.msixupload,而不是.msix——设置/p:UapAppxPackageBuildMode=StoreUpload以生成正确的上传格式。 -
在
windows-latest上进行的 CI 构建已包含 Windows SDK——无需为signtool.exe和MakeAppx.exe单独安装 SDK。 -
不要在 CI 示例中硬编码 TFM 路径——请使用变量引用(例如
${{ github.workspace }}),确保示例适用于不同的 .NET 版本。