dotnet-msix打包技能 dotnet-msix

这个技能提供了 .NET 桌面应用程序的 MSIX 打包完整解决方案,包括从 csproj 项目生成包、证书签名、分发渠道管理、自动更新配置和 CI/CD 集成。关键词:.NET, MSIX, 打包, 签名, 自动更新, Windows 应用, DevOps, CI/CD。

DevOps 0 次安装 0 次浏览 更新于 3/6/2026

name: dotnet-msix description: “打包 MSIX 应用程序。创建、签名、商店提交、App Installer 侧载、自动更新。” user-invocable: false

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+(当前稳定版)。MSIX 与 Windows App SDK 要求 Windows 10 build 19041+ 最低。App Installer 自动更新协议要求 Windows 10 build 1709+。.NET 8.0+ 基线。

范围

  • 从 csproj 和 WAP 项目创建包
  • 证书签名(自签名、受信任 CA、Store 签名)
  • Microsoft Store 提交工作流
  • App Installer 侧载和自动更新配置
  • 多架构的 MSIX 捆绑格式
  • CI/CD MSIX 构建步骤

不在范围内

  • WinUI 3 项目设置和 MSIX 与未打包比较 – 参见 [skill:dotnet-winui]
  • 原生 AOT MSBuild 配置 – 参见 [skill:dotnet-native-aot]
  • 通用 CI/CD 管道模式 – 参见 [skill:dotnet-gha-patterns] 和 [skill:dotnet-ado-patterns]
  • 通用 NuGet 打包 – 参见 [skill:dotnet-nuget-authoring]
  • 基于容器的部署 – 参见 [skill:dotnet-containers]

交叉引用:WinUI 项目设置和打包模式比较参见 [skill:dotnet-winui],AOT + MSIX 场景参见 [skill:dotnet-native-aot],CI 管道结构参见 [skill:dotnet-gha-patterns],ADO 管道结构参见 [skill:dotnet-ado-patterns],NuGet 打包参见 [skill:dotnet-nuget-authoring]。


MSIX 包创建

从 csproj(单项目打包)

现代 WinUI 3 和 Windows App SDK 应用可以直接从应用程序 .csproj 生成 MSIX 包,无需单独的 Windows Application Packaging(WAP)项目。

<!-- MyApp.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
    <UseWinUI>true</UseWinUI>

    <!-- MSIX 打包模式 -->
    <WindowsPackageType>MSIX</WindowsPackageType>

    <!-- 包身份 -->
    <AppxPackageDir>$(SolutionDir)AppPackages\</AppxPackageDir>
    <GenerateAppxPackageOnBuild>false</GenerateAppxPackageOnBuild>
  </PropertyGroup>
</Project>

构建 MSIX 包:

# 构建 MSIX 包
dotnet publish --configuration Release --runtime win-x64 \
  /p:GenerateAppxPackageOnBuild=true \
  /p:AppxPackageSigningEnabled=false

# 输出:AppPackages\MyApp_1.0.0.0_x64.msix

WAP 项目(桌面桥)

对于非 WinUI 桌面应用(WPF、WinForms),使用 Windows Application Packaging Project 将现有应用包装为 MSIX。WAP 项目(.wapproj)通过 Visual Studio “Windows Application Packaging Project” 模板创建 – 它们使用专门的项目格式,而非标准 Microsoft.NET.Sdk

关键配置是引用桌面应用项目:

<!-- MyApp.Package.wapproj(通过 VS 模板创建) -->
<!-- 生成的 .wapproj 文件中的关键元素: -->
<ItemGroup>
  <!-- 引用桌面应用项目以包含在 MSIX 中 -->
  <ProjectReference Include="..\MyWpfApp\MyWpfApp.csproj" />
</ItemGroup>

<!-- 在 .wapproj PropertyGroup 中设置目标平台版本 -->
<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 上签名才能安装。签名证书的主题必须与包清单中的 Publisher 属性匹配。

自签名证书(开发)

# 为开发创建自签名证书
$cert = New-SelfSignedCertificate `
  -Type Custom `
  -Subject "CN=My Company, O=My Company, L=Seattle, S=WA, C=US" `
  -KeyUsage DigitalSignature `
  -FriendlyName "MyApp 开发签名" `
  -CertStoreLocation "Cert:\CurrentUser\My" `
  -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", `
                    "2.5.29.19={text}")

# 导出 PFX 供 CI 使用
$password = ConvertTo-SecureString -String "$env:CERT_PASSWORD" -Force -AsPlainText
Export-PfxCertificate -Cert $cert `
  -FilePath "MyApp_DevSigning.pfx" `
  -Password $password

# 动态查找 signtool.exe(SDK 版本因机器而异)
$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

# 签名 MSIX 包
& $signtool sign /fd SHA256 /a /f "MyApp_DevSigning.pfx" `
  /p "$env:CERT_PASSWORD" `
  "AppPackages\MyApp_1.0.0.0_x64.msix"

受信任 CA 证书(生产)

对于 Microsoft Store 之外的生产分发:

  1. 从受信任 CA(DigiCert、Sectigo、GlobalSign)获取代码签名证书
  2. 主题必须匹配 Package.appxmanifest 中的 Publisher
  3. 时间戳签名 以确保长期有效性
# 使用受信任 CA 证书签名(从证书存储)
& signtool.exe sign /fd SHA256 /sha1 "THUMBPRINT_HERE" `
  /tr http://timestamp.digicert.com /td SHA256 `
  "AppPackages\MyApp_1.0.0.0_x64.msix"

# 使用 PFX 文件签名
& 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 分发不需要生产签名证书。

<!-- 对于 Store 提交,在开发期间使用测试证书 -->
<PropertyGroup>
  <AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>
  <PackageCertificateThumbprint>AUTO_GENERATED_BY_VS</PackageCertificateThumbprint>
</PropertyGroup>

证书要求摘要

分发 证书类型 需要受信任 CA
开发/测试 自签名 否(手动安装证书)
企业侧载 自签名或内部 CA 否(通过组策略部署证书)
直接下载 受信任 CA(如 DigiCert)
Microsoft Store 测试证书(由 MS 重新签名)

分发渠道

Microsoft Store 提交

  1. 在 Partner Center(https://partner.microsoft.com/dashboard)注册为 Windows 开发者
  2. 创建应用预订 以保留应用名称
  3. 构建与 Store 关联的 MSIX 包
# 构建用于 Store 提交(创建 .msixupload)
dotnet publish --configuration Release --runtime win-x64 \
  /p:GenerateAppxPackageOnBuild=true \
  /p:UapAppxPackageBuildMode=StoreUpload \
  /p:AppxBundle=Auto
  1. 通过 Partner Center 提交 .msixupload 文件
  2. Microsoft 验证、签名并发布应用

App Installer 侧载

App Installer(.appinstaller)支持直接分发和自动更新。将文件托管在 Web 服务器、网络共享或 CDN 上。

<!-- MyApp.appinstaller -->
<?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 本地管理设备
组策略 DISM / PowerShell 域加入设备
PowerShell Add-AppxPackage 基于脚本的部署
# 通过 PowerShell 进行企业部署
Add-AppxPackage -Path "\\fileserver\apps\MyApp_1.0.0.0_x64.msix"

# 为所有用户安装(需要管理员权限)
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" />

  <!-- 后台更新检查(Windows 10 1803+) -->
  <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 支持差异更新 – 仅下载更改的块。这在以下情况下自动进行:

  • 使用相同的 PackageNamePublisher
  • Version 递增
  • 新旧包托管在同一服务器上

差异更新无需额外配置。


MSIX 捆绑格式

MSIX 捆绑包(.msixbundle)将多个架构特定的 MSIX 包打包成单个可下载工件。Windows 自动安装正确的架构。

创建捆绑包

# 为多个架构构建
dotnet publish -c Release -r win-x64 /p:GenerateAppxPackageOnBuild=true
dotnet publish -c Release -r win-arm64 /p:GenerateAppxPackageOnBuild=true

# 动态查找 MakeAppx.exe(SDK 版本因机器而异)
$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

# 创建捆绑包
& $makeappx bundle /d "AppPackages" /p "MyApp_1.0.0.0.msixbundle"

MSBuild 捆绑包生成

<PropertyGroup>
  <!-- 在构建期间自动生成捆绑包 -->
  <AppxBundle>Always</AppxBundle>
  <AppxBundlePlatforms>x64|arm64</AppxBundlePlatforms>
</PropertyGroup>

捆绑包布局

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 特定构建步骤(嵌入到你的 CI 工作流中)
# 管道结构参见 [skill:dotnet-gha-patterns]
jobs:
  build-msix:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4

      - name: 设置 .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: 解码签名证书
        run: |
          $pfxBytes = [System.Convert]::FromBase64String("${{ secrets.SIGNING_CERT_BASE64 }}")
          [System.IO.File]::WriteAllBytes("signing-cert.pfx", $pfxBytes)
        shell: pwsh

      - name: 构建 MSIX 包
        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: 上传 MSIX 工件
        uses: actions/upload-artifact@v4
        with:
          name: msix-package
          path: AppPackages/**/*.msix

      - name: 清理证书
        if: always()
        run: Remove-Item -Path "signing-cert.pfx" -ErrorAction SilentlyContinue
        shell: pwsh

Azure DevOps MSIX 构建

# MSIX 特定构建步骤(嵌入到你的 ADO 管道中)
# 管道结构参见 [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: '构建 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 版本 构建号
MSIX 与 Windows App SDK 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 捆绑格式 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>

代理注意事项

  1. 清单中的 Publisher 必须完全匹配签名证书的主题 – 不匹配会导致构建或签名时出现 SignTool Error: SignerSign() failed

  2. 自签名证书需要手动信任安装 – 用户必须在 MSIX 安装前将证书安装到 Trusted PeopleTrusted Root Certification Authorities

  3. 切勿将 PFX 文件或证书密码提交到源代码控制 – 将证书存储为 CI 机密(GitHub Secrets、Azure DevOps Secure Files)并在构建管道期间解码。

  4. AppxBundle=Auto 仅在构建多个架构时生成捆绑包 – 对于单架构构建,它生成扁平 .msix 文件,而非捆绑包。

  5. MSIX 应用在类似容器的沙箱中运行 – 文件系统访问被虚拟化。应用写入 AppData 时会被重定向到包特定位置。使用 ApplicationData.Current API,而非硬编码路径。

  6. Store 提交使用 .msixupload 而非 .msix – 设置 /p:UapAppxPackageBuildMode=StoreUpload 以生成正确的上传格式。

  7. CI 构建在 windows-latest 上包含 Windows SDK – 无需单独安装 SDK 步骤以获取 signtool.exeMakeAppx.exe

  8. 不要在 CI 示例中硬编码 TFM 路径 – 使用变量引用(例如,${{ github.workspace }})以使示例跨 .NET 版本工作。