创建托管应用

从 Windows 10 版本 2004 开始,你可以创建托管应用。 托管应用与父主机应用共享相同的可执行文件和定义,但其外观和行为类似于系统上的单独应用。

对于希望组件(如可执行文件或脚本文件)的行为类似于独立 Windows 10 应用,但该组件需要托管进程才能进行执行的应用场景,托管应用会很有用。 例如,PowerShell 或 Python 脚本可以作为托管应用交付,需要安装主机才能运行。 托管应用可以具有其自己的开始磁贴、标识,且可以与 Windows 10 功能(如后台任务、通知、磁贴和共享目标)深度集成。

包清单中的多个元素和属性支持托管应用程序功能,这些元素和属性使托管应用能够使用主机应用包中的可执行文件和定义。 当用户运行托管应用时,操作系统会在托管应用的标识下自动启动主机可执行文件。 然后,主机可以加载视觉资产、内容或调用 API 作为托管应用。 托管应用获取在主机和托管应用之间声明的功能的交集。 这意味着托管应用不能要求比主机提供的功能更多的功能。

定义主机

主机是托管应用的主要可执行文件或运行时进程。 目前,唯一受支持的主机是具有包标识的桌面应用程序(.NET 或 C++ 桌面)。 桌面应用有几种方法来获得包标识:

主机由 uap10:HostRuntime 扩展在其包清单中声明。 此扩展有一个 Id 属性,必须为其分配一个值,该值也被托管应用的包清单引用。 当托管应用被激活时,主机以托管应用的身份启动,并且可以从托管应用包中加载内容或二进制文件。

以下示例演示了如何在包清单中定义主机。 uap10:HostRuntime 扩展是包范围的,因此被声明为 Package 元素的子元素。

<Package xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10">

  <Extensions>
    <uap10:Extension Category="windows.hostRuntime"  
        Executable="PyScriptEngine\PyScriptEngine.exe"  
        uap10:RuntimeBehavior="packagedClassicApp"  
        uap10:TrustLevel="mediumIL">
      <uap10:HostRuntime Id="PythonHost" />
    </uap10:Extension>
  </Extensions>

</Package>

记下有关以下元素的这些重要细节。

元素 详细信息
uap10:Extension windows.hostRuntime 类别声明了一个包范围的扩展,该扩展定义了在激活托管应用时要使用的运行时信息。 托管应用将使用扩展中声明的定义运行。 当使用前面示例中声明的主机应用时,托管的应用将作为可执行的 PyScriptEngine.exe 在 mediumIL 信任级别运行。

Executable、uap10:RuntimeBehavior 和 uap10:TrustLevel 属性指定包中主机进程二进制文件的名称以及托管应用程序的运行方式。 例如,使用上一个示例中的属性的托管应用将作为可执行 PyScriptEngine.exe 在 mediumIL 信任级别运行。
uap10:HostRuntime Id 属性声明了包中这个特定主机应用的唯一标识符。 一个包可以有多个主机应用程序,每个应用程序都必须有一个具有唯一 Id 的 uap10:HostRuntime 元素。

声明托管应用

托管应用声明对主机的包依赖。 托管应用利用主机 ID(即主机包中 uap10:HostRuntime 扩展的 Id 属性)进行激活,而不是在其自己的包中指定可执行的入口点。 托管应用通常包含主机可以访问的内容、视觉资产、脚本或二进制文件。 托管应用包中的 TargetDeviceFamily 值应以与主机相同的值作为目标。

托管的应用包可以签名或未签名:

  • 签名包可能包含可执行文件。 这在具有二进制扩展机制的场景中很有用,它使主机能够加载托管应用包中的 DLL 或注册组件。
  • 在大多数情况下,未签名的包将包含可执行内容。 但是,如果主机仅需要加载图像、资产以及内容或脚本文件,则仅包含 非可执行文件 的未签名包非常有用。 未签名的包必须在其 Identity 元素中包含特殊OID值,否则将不允许它们注册。 这可以防止未签名包与签名包的身份发生冲突或欺骗。

若要定义托管应用,请在包清单中声明以下项:

以下示例演示了未签名托管应用的包清单的相关部分。

<Package xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10">

  <Identity Name="NumberGuesserManifest"
    Publisher="CN=AppModelSamples, OID.2.25.311729368913984317654407730594956997722=1"
    Version="1.0.0.0" />

  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.19041.0" />
    <uap10:HostRuntimeDependency Name="PyScriptEnginePackage" Publisher="CN=AppModelSamples" MinVersion="1.0.0.0"/>
  </Dependencies>

  <Applications>
    <Application Id="NumberGuesserApp"  
      uap10:HostId="PythonHost"  
      uap10:Parameters="-Script &quot;NumberGuesser.py&quot;">
    </Application>
  </Applications>

</Package>

记下有关以下元素的这些重要细节。

元素 详细信息
标识 由于此示例中托管的应用包未签名,因此 Publisher 属性必须包含 OID.2.25.311729368913984317654407730594956997722=1 字符串。 这确保了未签名包不能欺骗已签名包的身份。
TargetDeviceFamily MinVersion 属性必须指定 10.0.19041.0 或更高的操作系统版本。
uap10:HostRuntimeDependency 此元素声明对主机应用包的依赖关系。 这包括主机包的名称和发布者,以及它所依赖的 MinVersion。 这些值可以在主机包中的 Identity 元素下找到。
应用程序 uap10:HostId 属性表示对主机的依赖关系。 托管的应用包必须声明此属性,而不是 ApplicationExtension 元素的通常 Executable 和 EntryPoint 属性。 因此,托管应用从具有相应 HostId 值的主机继承 Executable 、 EntryPoint 和 runtime 属性。

uap10:Parameters 属性指定传递给主机可执行文件的入口点函数的参数。 因为主机需要知道如何处理这些参数,所以主机和托管之间存在隐含的契约应用。

运行时注册未签名的托管应用包

uap10:HostRuntime 扩展的一个好处是它使主机能够在运行时动态生成托管应用包并使用 PackageManager API 注册它,而无需对其进行签名。 这使主机能够为托管应用包动态生成内容和清单,然后注册包。

使用 PackageManager 类的以下方法来注册未签名的托管应用包。 从 Windows 10 版本 2004 开始已提供这些方法。

  • AddPackageByUriAsync:使用 options 参数的 AllowUnsigned 属性注册未签名的 MSIX 包。
  • RegisterPackageByUriAsync:执行松散包清单文件注册。 如果包已签名,则包含清单的文件夹必须包含 .p7x 文件和目录。 如果未签名,则必须设置 options 参数的 AllowUnsigned 属性。

未签名托管应用的要求

  • 包清单中的 ApplicationExtension 元素不能包含激活数据,例如 Executable、 EntryPoint 或 TrustLevel 属性。 相反,这些元素只能包含一个 uap10:HostId 属性,该属性表示对主机的依赖关系和一个 uap10:Parameters 属性。
  • 包必须是主包。 不能是捆绑包、框架包、资源或可选包。

安装和注册未签名托管应用包的主机的要求

  • 主机必须具有包标识。
  • 主机必须具有 packageManagement 受限功能
    <rescap:Capability Name="packageManagement" />
    

示例

有关将自己声明为主机并在运行时动态注册托管应用包的完整功能示例应用,请参阅托管应用示例

主机

主机名为 PyScriptEngine。 这是一个用 C# 编写的运行 python 脚本的包装器。 使用 -Register 参数运行时,脚本引擎会安装一个包含 python 脚本的托管应用。 当用户尝试启动新安装的托管应用时,主机将启动并执行 NumberGuesser python 脚本。

主机应用的包清单(PyScriptEnginePackage 文件夹中的 Package.appxmanifest 文件)包含一个 uap10:HostRuntime 扩展,它将应用声明为具有 ID PythonHost 和可执行 PyScriptEngine.exe 的主机。

注意

在此示例中,包清单名为 Package.appxmanifest,它是 Windows 应用程序打包项目的一部分。 构建此项目时,它会生成一个名为 AppxManifest.xml 的清单并为主机应用构建 MSIX 包。

托管应用

托管应用由 python 脚本和包工件(如包清单)组成。 它不包含任何 PE 文件。

托管应用的包清单(NumberGuesser/AppxManifest.xml 文件)包含以下项:

  • Identity 元素的 Publisher 属性包含 OID.2.25.311729368913984317654407730594956997722=1 标识符,这是未签名包所必需的。
  • Application 元素的 uap10:HostId 属性将 PythonHost 标识为其主机。

运行示例

该示例需要 10.0.19041.0 或更高版本的 Windows 10 和 Windows SDK。

  1. 示例下载到开发计算机上的文件夹。

  2. 在 Visual Studio 中打开 PyScriptEngine.sln 解决方案,并将 PyScriptEnginePackage 项目设置为启动项目。

  3. 生成 PyScriptEnginePackage 项目。

  4. 在“解决方案资源管理器”中,右键单击 PyScriptEnginePackage 项目并选择“发布”。

  5. 打开命令提示符窗口并转到你复制示例文件的目录并运行以下命令来注册示例 NumberGuesser 应用(托管应用)。 将 D:\repos\HostedApps 切换到复制示例文件的路径。

    D:\repos\HostedApps>pyscriptengine -Register D:\repos\HostedApps\NumberGuesser\AppxManifest.xml
    

    注意

    你可以在命令行上运行 pyscriptengine,因为示例中的主机声明了 AppExecutionAlias

  6. 打开开始菜单并单击 NumberGuesser 以运行托管应用。