依赖于框架的使用外部位置打包的应用或未打包应用的 Windows App SDK 部署指南

本主题提供有关部署打包到外部位置或未打包、以及使用 Windows 应用 SDK 的应用的指南。

  • 此类应用是桌面应用(而不是 UWP 应用)。
  • 可以使用 .NET 语言(如 C# 或 C++)编写它们。
  • 对于用户界面,他们可以使用 WinUI 3、WPF、WinForms 或其他 UI 框架。

概述

打包到外部位置和未打包应用的开发人员负责将所需的Windows 应用 SDK 运行时程序包部署到其最终用户。 这可以通过运行安装程序或通过直接安装 MSIX 包来完成。 下面部署 Windows 应用 SDK 运行时部分更详细地介绍了这些选项。

打包到外部位置和未打包的应用也有额外的运行时要求。 必须使用 Bootstrapper API 初始化对 Windows 应用 SDK 运行时的访问权限。 此外,如果应用使用除 Windows 应用 SDK 之外的其他框架包,则可以使用动态依赖项 API。 下面的打包到外部位置或未打包的应用的运行时要求部分更详细地描述了这些要求。

先决条件

其他先决条件

  • Windows 应用 SDK 的实验版预览版需要启用旁加载才能安装运行时。
    • Windows 10 版本 2004 及更高版本上会自动启用旁加载。

    • 如果你的开发计算机或部署计算机正在运行 Windows 11,请确认是否启用了旁加载:

      • 设置>隐私和安全>针对开发人员。 确保开发人员模式设置已打开。
    • 如果你的开发计算机或部署计算机正在运行 Windows 10 版本 1909 或更低版本,请确认是否启用了旁加载:

      • 设置>隐私和安全>针对开发人员>使用开发人员功能。 确认已选择旁加载应用开发人员模式
    • 开发人员模式设置包括旁加载和其他功能。

      注意

      如果计算机在企业环境中管理,则可能存在阻止这些设置更改的策略。 在这种情况下,如果你或你的应用尝试安装 Windows 应用 SDK 运行时时遇到错误,请联系 IT 专业人员以启用旁加载或开发人员模式

部署 Windows 应用 SDK 运行时

打包到外部位置和未打包的应用有两个选项来部署 Windows 应用 SDK 运行时:

  • 选项 1:使用安装程序:无提示安装程序分发所有 Windows 应用 SDK MSIX 包。 每个 X64X86Arm64 体系结构都有单独的安装程序。
  • 选项 2:直接安装包:你可以让现有的设置或 MSI 工具携带并安装 Windows 应用 SDK 的 MSIX 包。

选项 1:使用安装程序

可以通过运行安装程序来部署所有 Windows 应用 SDK 运行时程序包。 Windows 应用 SDK 的下载中提供了安装程序。 运行 installer (.exe) 时,可能会看到类似于以下输出:

Deploying package: Microsoft.WindowsAppRuntime.1.0_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0

Deploying package: Microsoft.WindowsAppRuntime.1.0_0.318.928.0_x86__8wekyb3d8bbwe
Package deployment result : 0x0

Deploying package: MicrosoftCorporationII.WindowsAppRuntime.Main.1.0_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

Deploying package: Microsoft.WindowsAppRuntime.Singleton_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

Deploying package: Microsoft.WinAppRuntime.DDLM.0.318.928.0-x6_0.318.928.0_x64__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

Deploying package: Microsoft.WinAppRuntime.DDLM.0.318.928.0-x8_0.318.928.0_x86__8wekyb3d8bbwe
Package deployment result : 0x0
Provisioning result : 0x0

All install operations successful.

您可以使用 --quiet 选项在没有用户交互的情况下运行安装程序,并抑制所有文本输出:

WindowsAppRuntimeInstall.exe --quiet

还可以使用 --force 选项选择强制更新 MSIX 包,并关闭当前正在运行的任何 Windows 应用 SDK 进程。 1.1 中引入了此功能。

WindowsAppRuntimeInstall.exe --force

要查看所有安装程序命令行选项,运行 WindowsAppRuntimeInstall --h

安装完成后,可以运行打包到外部位置或未打包的应用。 关于如何构建并运行使用 Windows 应用 SDK 的打包到外部位置或未打包应用的示例,请参阅教程:在使用Windows 应用 SDK 的打包到外部位置或未打包的应用中使用引导程序 API

将 Windows 应用 SDK 安装程序链接到应用的设置

如果你的应用具有自定义安装程序,则可以在应用的安装过程中链接 Windows 应用 SDK 安装过程。 Windows 应用 SDK 安装程序当前不提供默认 UI,因此你需要使用设置的自定义 UI 进行链接。

你可以静默启动和跟踪 Windows 应用 SDK 设置,同时使用 ShellExecute 显示你自己的设置进度视图。 Windows 应用 SDK 安装程序以静默方式解压缩 Windows 应用 MSIX 捆绑包并调用 PackageManager.AddPackageAsync 方法来完成安装。 这与你可能使用过的其他运行时安装程序非常相似,例如 .NET、Visual C++ 或 DirectX。

有关演示如何从安装程序运行 Windows 应用 SDK 安装程序的代码示例,请参阅安装程序功能测试中的 RunInstaller 功能。

安装程序示例

请参阅以下示例,了解如何在安装过程中从 Win32 安装程序启动安装程序,而无需弹出控制台窗口:

故障排除

返回代码

下表列出了 Windows 应用 SDK .exe 安装程序最常见的返回码。 所有版本的安装程序的返回代码都是相同的。

返回代码 说明
0x0 包安装或预配已成功完成。
0x80073d06 一个或多个包安装失败。
0x80070005 无法进行系统范围的安装或配置,因为应用没有运行提升或执行安装的用户没有管理员权限。

安装错误

如果 Windows 应用 SDK 安装程序在安装过程中返回错误,它将返回描述问题的错误代码。

选项 2:直接部署 Windows 应用 SDK 运行时包

作为使用 Windows 应用 SDK 安装程序部署到最终用户的替代方法,你可以通过应用的程序或 MSI 手动部署 MSIX 包。 此选项最适合希望获得更多控制权的开发人员。

有关演示安装程序如何安装 MSIX 包的示例,请参阅 Windows 应用 SDK 安装程序代码中的 install.cpp

若要检查是否已安装Windows 应用 SDK(如果是,则为哪个版本),可以通过调用 PackageManager.FindPackagesForUserWithPackageTypes 来检查特定包系列。

在 mediumIL(完全信任)解压缩进程(请参阅 Application 元素)中,可以使用以下代码检查注册到当前用户的包:

using Windows.Management.Deployment;

public class WindowsAppSDKRuntime
{
    public static IsPackageRegisteredForCurrentUser(
        string packageFamilyName,
        PackageVersion minVersion,
        Windows.System.ProcessorArchitecture architecture,
        PackageTypes packageType)
    {
        ulong minPackageVersion = ToVersion(minVersion);

        foreach (var p : PackageManager.FindPackagesForUserWithPackageTypes(
            string.Empty, packageFamilyName, packageType)
        {
            // Is the package architecture compatible?
            if (p.Id.Architecture != architecture)
            {
                continue;
            }

            // Is the package version sufficient for our needs?
            ulong packageVersion = ToVersion(p.Id.Version);
            if (packageVersion < minPackageVersion)
            {
                continue;
            }

            // Success.
            return true;
        }

        // No qualifying package found.
        return false;
    }

    private static ulong ToVersion(PackageVersion packageVersion)
    {
        return ((ulong)packageVersion.Major << 48) |
               ((ulong)packageVersion.Minor << 32) |
               ((ulong)packageVersion.Build << 16) |
               ((ulong)packageVersion.Revision);
    }
}

对于上述方案,调用 FindPackagesForUserWithPackageTypes 最好调用 FindPackagesForUser。 这是因为你可以将搜索范围缩小到(在本示例中),只需 框架 或主 。 这可以避免匹配其他类型的包(如 资源可选捆绑包),而此示例对此不感兴趣。

若要使用当前/调用用户上下文,请将 userSecurityId 参数设置为空字符串。

现在,一些信息可帮助你决定 如何在 上面的代码示例中调用函数。 正确安装的运行时由多个包组成,这些包依赖于系统的 CPU 体系结构:

  • 在 x86 计算机上:Fwk=[x86]、Main=[x86]、Singleton=[x86]、DDLM=[x86]。
  • 在 x64 计算机上:Fwk=[x86、x64]、Main=[x64]、Singleton=[x64]、DDLM=[x86、x64]。
  • 在 arm64 计算机上:Fwk=[x86、x64、arm64]、Main=[arm64]、Singleton=[arm64]、DDLM=[x86、x64、arm64]。

对于 MainSingleton 包,其体系结构应与系统的 CPU 体系结构匹配;例如,x64 系统上的 x64 包。 对于框架包,x64 系统可以同时运行 x64 和 x86 应用;同样,arm64 系统可以运行 arm64、x64 和 x86 应用。 DDLM 包检查类似于框架检查,不同之处在于PackageType=main,由于 DDLM 的唯一命名方案,packagefamilyname 不同,并且多个 (不同) packagefamilyname 可能适用。 有关详细信息,请参阅 MSIX 包 规范。因此,检查如下所示:

public static bool IsRuntimeRegisteredForCurrentUser(PackageVersion minVersion)
{
    ProcessorArchitecture systemArchitecture = DetectSystemArchitecture();

    return IsFrameworkRegistered(systemArchitecture, minVersion) &&
           IsMainRegistered(systemArchitecture, minVersion) &&
           IsSingletonRegistered(systemArchitecture, minVersion) &&
           IsDDLMRegistered(systemArchitecture, minVersion);
}

private static ProcecssorArchitecture DetectSystemArchitecture()
{
    // ...see the call to IsWow64Process2(), and how the result is used...
    // ...as per `IsPackageApplicable()` in
    // [install.cpp](https://github.com/microsoft/WindowsAppSDK/blob/main/installer/dev/install.cpp)
    // line 99-116...
    // ...WARNING: Use IsWow64Process2 to detect the system architecture....
    // ...         Other similar APIs exist, but don't give reliably accurate results...
}

private static bool IsFrameworkRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    // Check x86.
    if (!IsPackageRegisteredForCurrentUser(
        global::Microsoft.WindowsAppSDK.Runtime.Packages.Framework.PackageFamilyName,
        minVersion, ProcessorArchitecture.X86,
        PackageTypes.Framework))
    {
        return false;
    }

    // Check x64 (if necessary).
    if ((systemArchitecture == ProcessorArchitecture.X64) || 
        (systemArchitecture == ProcessorArchitcture.Arm64))
    {
        if (!IsPackageRegisteredForCurrentUser(
            global::Microsoft.WindowsAppSDK.Runtime.Packages.Framework.PackageFamilyName,
            minVersion, ProcessorArchitecture.X64,
            PackageTypes.Framework))
        {
            return false;
        }
    }

    // Check arm64 (if necessary).
    if (systemArchitecture == ProcessorArchitcture.Arm64)
    {
        if (!IsPackageRegisteredForCurrentUser(
            global::Microsoft.WindowsAppSDK.Runtime.Packages.Framework.PackageFamilyName,
            minVersion, ProcessorArchitecture.Arm64,
            PackageTypes.Framework))
        {
            return false;
        }
    }

    return true;
}

private static bool IsMainRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    return IsPackageRegisteredForCurrentUser(
        global::Microsoft.WindowsAppSDK.Runtime.Packages.Main.PackageFamilyName,
        minVersion,
        systemArchitecture,
        PackageTypes.Main);
}

private static bool IsSingletonRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    return IsPackageRegisteredForCurrentUser(
        global::Microsoft.WindowsAppSDK.Runtime.Packages.Singleton.PackageFamilyName,
        minVersion,
        systemArchitecture,
        PackageTypes.Main);
}

private static bool IsDDLMRegistered(ProcessorArchitecture systemArchitecture,
    PackageVersion minVersion)
{
    // ...similar to IsFrameworkRegistered, but the packageFamilyName is more complicated...
    // ...and no predefined constant is currently available...
    // ...for more details, see
    // https://github.com/microsoft/WindowsAppSDK/blob/main/specs/Deployment/MSIXPackages.md.
}

上面的信息和代码涵盖了基本检测方案。 若要检测是否为所有用户预配运行时,或从应用容器执行上述操作,以及/或从打包 mediumIL 进程执行此操作,需要其他逻辑。

部署方案

  • 在系统范围内安装 Windows 应用 SDK 运行时:系统范围内的安装会为所有用户更改机器,包括将来添加的新用户。 如果应用正在特权运行并且执行安装的用户具有管理员权限,则安装程序将通过调用 ProvisionPackageForAllUsersAsync 在系统范围内注册 MSIX 包。 如果系统范围的注册不成功,将只为当前执行安装的用户执行安装。 在托管企业环境中,IT 管理员应该能够像往常一样为每个人进行预配。

  • Windows 应用 SDK安装程序重新分发的体系结构:Windows 应用 SDK安装程序在体系结构和Arm64体系结构中x86x64可用。 安装程序的每个版本仅包含其命名的特定体系结构的 MSIX 包。 例如,如果在 x64 或 Arm64 设备上运行x86WindowsAppRuntimeInstall.exe,则该x86安装程序将仅部署到该设备的 x86 体系结构包。

  • 所有 Windows 应用 SDK MSIX 包都已安装在计算机上:MSIX 包安装到系统范围的位置,磁盘上只有一个副本。 如果应用在计算机上已安装所有 MSIX 包依赖项时尝试安装 Windows 应用 SDK,则不执行安装。

  • 计算机上未安装一个或多个 Windows 应用 SDK MSIX 包:部署 Windows 应用 SDK 时,始终尝试安装所有 MSIX 包(framework、main、singleton、DDLM)以确保安装所有依赖项并且你可以避免中断最终用户体验。

打包到外部位置或未打包的应用的运行时要求

打包到外部位置或未打包的应用有额外的运行时要求才能使用 Windows 应用 SDK 运行时。 这涉及到在运行时引用和初始化 Windows 应用 SDK Framework 包。 此外,动态依赖项 API 还可用于引用 Windows 应用 SDK 之外的其他框架包。

使用 Windows 应用 SDK 运行时

打包到外部位置和未打包的应用必须调用 Bootstrapper API 才能在运行时使用 Windows 应用 SDK。 在应用能使用 WinUI、App Lifecycle、MRT Core 和 DWriteCore 等 Windows 应用 SDK 功能之前,这是必须的。 引导程序组件允许打包到外部位置和未打包的应用执行以下重要任务:

  • 查找 Windows 应用 SDK 框架包,并加载到应用的包图中。
  • 为 Windows 应用 SDK 框架包初始化动态依赖项生存期管理器 (DDLM)。 DDLM 的用途是防止在 Windows 应用 SDK 框架包被打包到外部位置或未打包的应用使用时提供服务。

为打包到外部位置或未打包的应用加载 Windows 应用 SDK 运行时最简单的方法是在项目文件 (.csproj 或 .vcxproj) 中设置 <WindowsPackageType>None</WindowsPackageType> 属性。 还可以直接在应用的启动代码中调用引导程序 API,以便更好地控制初始化。 有关详细信息,请参阅对使用外部位置打包或未打包的应用使用 Windows 应用 SDK 运行时教程:在使用 Windows 应用 SDK 的使用外部位置打包或未打包的应用中使用引导程序 API

动态依赖项支持使打包到外部位置和未打包的应用能够保留其现有的部署机制,例如 MSI 或任何安装程序,并能够在其应用程序中利用 Windows 应用 SDK。 打包的、打包到外部位置的和未打包的应用都可以使用动态依赖项,尽管动态依赖项主要用于打包到外部位置的和未打包的应用。

Windows 应用 SDK 框架包的每个版本和体系结构都有一个 DDLM。 这意味着在 x64 计算机上,你可能同时具有 x86x64 版本的 DDLM 来支持这两种体系结构的应用。

使用动态依赖项 API 引用其他框架包

要在 Windows 应用 SDK(例如 DirectX)之外的其他框架包中使用功能,则打包到外部位置和未打包的应用可以调用动态依赖项 API。 除了引导程序组件,Windows 应用 SDK 还提供了更广泛的 C/C++ 函数集和实现动态依赖 API 的 WinRT 类。 此 API 旨在用于在运行时动态引用任何框架包。

有关详细信息,请参阅从桌面应用动态使用 MSIX 框架包动态依赖项示例

将 .winmd 文件部署到目标计算机

除了应用,我们建议继续部署 Windows 元数据 (.winmd) 文件。 元数据可由运行时的各种 API 和行为使用,并且其缺失可能会限制或中断功能。 例如,元数据可用于跨单元边界封送对象;并且封送的需要可能是计算机性能的一种功能。 由于没有确定性的方式来了解是否需要元数据,因此除非非常关心大小,否则应该部署 .winmd