测试用于应用附加的 MSIX 包

本文介绍如何在 Azure 虚拟桌面外部装载 MSIX 包,以帮助测试用于应用附加的包。 支持应用附加的 API 适用于 Windows 11 企业版和 Windows 10 企业版。 这些 API 可在 Azure 虚拟桌面外部用于测试,但是 Azure 虚拟桌面外部没有用于应用附加或 MSIX 应用附加的管理平面。

有关应用附加和 MSIX 应用附加的详细信息,请参阅 Azure 虚拟桌面中的应用附加和 MSIX 应用附加

先决条件

在测试某个包以按照本文中的说明操作之前,需要满足以下条件:

不需要 Azure 虚拟桌面部署,因为本文介绍的是在 Azure 虚拟桌面外部进行测试的过程。

注意

Microsoft 支持部门不支持 CimDiskImage PowerShell 模块,因此如果遇到任何问题,则需要在模块的 GitHub 存储库上提交请求。

阶段

若要在 Azure 虚拟桌面外部使用 MSIX 包,必须按以下顺序执行四个不同的阶段:

  1. 阶段
  2. 注册
  3. 取消注册
  4. 转储

暂存和取消暂存是计算机级别的操作,而注册和取消注册是用户级别的操作。 需要使用的命令因所使用的 PowerShell 版本以及磁盘映像是采用 CimFS、VHDX 还是 VHD 格式而异

注意

所有 MSIX 包都包含证书。 你负责确保 MSIX 包的证书在环境中受信任。

准备暂存 MSIX 包

暂存脚本为计算机接收 MSIX 包做好准备,并将相关的包装载到计算机。

为正在使用的 PowerShell 版本选择相关选项卡。

若要使用 PowerShell 6 或更高版本暂存包,需要在暂存操作之前运行以下命令,才能将 Windows 运行时包的功能引入 PowerShell。

  1. 以管理员身份打开 PowerShell 命令提示符。

  2. 运行以下命令以下载和安装 Windows 运行时包。 每个计算机只需运行以下命令一次。

    #Required for PowerShell 6 and later
    $nuGetPackageName = 'Microsoft.Windows.SDK.NET.Ref'
    Register-PackageSource -Name MyNuGet -Location https://www.nuget.org/api/v2 -ProviderName NuGet
    Find-Package $nuGetPackageName | Install-Package
    
  3. 接下来,运行以下命令,使 Windows 运行时组件在 PowerShell 中可用:

    #Required for PowerShell 6 and later
    $nuGetPackageName = 'Microsoft.Windows.SDK.NET.Ref'
    $winRT = Get-Package $nuGetPackageName
    $dllWinRT = Get-ChildItem (Split-Path -Parent $winRT.Source) -Recurse -File WinRT.Runtime.dll
    $dllSdkNet = Get-ChildItem (Split-Path -Parent $winRT.Source) -Recurse -File Microsoft.Windows.SDK.NET.dll
    Add-Type -AssemblyName $dllWinRT.FullName
    Add-Type -AssemblyName $dllSdkNet.FullName
    

暂存 MSIX 包

准备好计算机来暂存 MSIX 包后,需要装载磁盘映像,然后完成 MSIX 包的暂存。

装载磁盘映像

装载磁盘映像的过程因是使用 CimFs、VHDX 还是 VHD 格式的磁盘映像而异。 选择所使用格式的相关选项卡。

若要装载 CimFS 磁盘映像,请运行以下命令:

  1. 在同一 PowerShell 会话中,运行以下命令:

    $diskImage = "<Local or UNC path to the disk image>"
    
    $mount = Mount-CimDiskImage -ImagePath $diskImage -PassThru -NoMountPath
    
    #We can now get the Device Id for the mounted volume, this will be useful for the destage step.
    $deviceId = $mount.DeviceId
    Write-Output $deviceId
    
  2. 保留变量 $deviceId。 本文中后面的步骤需要此信息。

  3. 完成后,继续完成磁盘映像的暂存

完成磁盘映像的暂存

最后,需要为所有映像格式运行以下命令才能完成磁盘映像的暂存。 此命令使用你在上一节中装载磁盘映像时创建的 $deviceId 变量。

  1. 在同一 PowerShell 会话中,运行以下命令检索应用程序信息:

    $manifest = Get-ChildItem -LiteralPath $deviceId -Recurse -File AppxManifest.xml
    $manifestFolder = $manifest.DirectoryName
    
  2. 运行以下命令获取 MSIX 包全名并将其存储在变量中。 后续步骤需要此变量。

    $msixPackageFullName = $manifestFolder.Split('\')[-1]
    Write-Output $msixPackageFullName
    
  3. 通过运行以下命令为包管理器 API 的清单文件夹创建绝对 URI:

    $folderUri = $maniFestFolder.Replace('\\?\','file:\\\')
    $folderAbsoluteUri = ([Uri]$folderUri).AbsoluteUri
    
  4. 运行以下命令,使用绝对 URI 暂存应用程序包:

    $asTask = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.ToString() -eq 'System.Threading.Tasks.Task`1[TResult] AsTask[TResult,TProgress](Windows.Foundation.IAsyncOperationWithProgress`2[TResult,TProgress])' })[0]
    $asTaskAsyncOperation = $asTask.MakeGenericMethod([Windows.Management.Deployment.DeploymentResult], [Windows.Management.Deployment.DeploymentProgress])
    
    $packageManager = New-Object -TypeName Windows.Management.Deployment.PackageManager
    
    $asyncOperation = $packageManager.StagePackageAsync($folderAbsoluteUri, $null, "StageInPlace")
    
  5. 通过运行以下命令监视应用程序包的暂存进度。 暂存包所需的时间取决于其大小。 转移完成后,$stagingResult 变量的 Status 属性将为 RanToCompletion

    $stagingResult = $asTaskAsyncOperation.Invoke($null, @($asyncOperation))
    
    while ($stagingResult.Status -eq "WaitingForActivation") {
        Write-Output "Waiting for activation..."
        Start-Sleep -Seconds 5
    }
    
    Write-Output $stagingResult
    

暂存 MSI 包后,可以注册 MSIX 包。

注册 MSIX 包

若要注册 MSIX 包,请在同一 PowerShell 会话中运行以下命令。 此命令使用在上一节中创建的 $msixPackageFullName 变量。

$manifestPath = Join-Path (Join-Path $Env:ProgramFiles 'WindowsApps') (Join-Path $msixPackageFullName AppxManifest.xml)
Add-AppxPackage -Path $manifestPath -DisableDevelopmentMode -Register

注册 MSIX 包后,你的应用程序应该可在会话中使用。 现在可以打开该应用程序进行测试和故障排除。 完成后,需要注销 MSIX 包并取消其暂存。

取消注册 MSIX 包

完成 MSIX 包并准备好将其删除后,首先需要将其取消注册。 若要取消暂存 MSIX 包,请在同一 PowerShell 会话中运行以下命令。 这些命令再次获取磁盘的 DeviceId 参数,并使用上一节中创建的 $msixPackageFullName 变量移除包。

$appPath = Join-Path (Join-Path $Env:ProgramFiles 'WindowsApps') $msixPackageFullName
$folderInfo = Get-Item $appPath
$deviceId = '\\?\' + $folderInfo.Target.Split('\')[0] +'\'
Write-Output $deviceId #Save this for later

Remove-AppxPackage $msixPackageFullName -PreserveRoamableApplicationData

取消暂存 MSIX 包

最后,要取消暂存 MSIX 包,需要卸载磁盘映像,在同一 PowerShell 会话中运行以下命令,以确保该包仍然没有为任何用户注册。 此命令使用在上一节中创建的 $msixPackageFullName 变量。

Remove-AppxPackage -AllUsers -Package $msixPackageFullName -ErrorAction SilentlyContinue

卸下磁盘映像

若要完成取消暂存过程,需要从系统中卸载磁盘。 需要使用的命令取决于磁盘映像的格式。 选择所使用格式的相关选项卡。

若要卸载 CimFS 磁盘映像,请在同一 PowerShell 会话中运行以下命令:

Dismount-CimDiskImage -DeviceId $deviceId

卸载完磁盘后,可以安全地删除 MSIX 包。

为 MSIX 应用附加代理设置模拟脚本

如果要自动向设备添加和删除 MSIX 包,可以使用本文中的 PowerShell 命令创建在启动、登录、注销和关闭时运行的脚本。 若要了解详细信息,请参阅在组策略中使用启动、关闭、登录和注销脚本。 需要确保每个阶段所需的任何变量在每个脚本中都可用。

为每个阶段创建一个脚本:

  • 启动脚本运行暂存过程
  • 登录脚本运行注册过程
  • 注销脚本运行取消注册过程
  • 关闭脚本运行取消暂存过程

注意

可以使用任务计划程序来运行暂存脚本。 若要运行脚本,请将任务触发器设置为“当计算机启动时”,然后启用“以最高权限运行”

脱机使用包

如果通过未连接到 Internet 的设备使用适用于企业的 Microsoft Store适用于教育的 Microsoft Store 中的包,则需要从 Microsoft Store 获取包许可证并将其安装到设备上,才能成功运行该应用。 如果设备处于联机状态并且可以连接到适用于企业的 Microsoft Store,就会自动下载所需的许可证,但如果处于脱机状态,则需要手动设置许可证。

若要安装许可证文件,需要使用 PowerShell 脚本来调用 WMI Bridge 提供程序中的 MDM_EnterpriseModernAppManagement_StoreLicenses02_01 类。

下面介绍如何设置脱机使用的许可证:

  1. 从适用于企业的 Microsoft Store 下载应用包、许可证和必需的框架。 你需要编码和未编码的许可证文件。 若要了解如何下载脱机许可的应用,请参阅分发脱机应用

  2. 以管理员身份运行以下 PowerShell 命令。 可以在暂存阶段结束时安装许可证。 需要编辑以下变量:

    • $contentID 是未编码的许可证文件 (.xml) 中的 ContentID 值。 可以在所选的文本编辑器中打开许可证文件。

    • $licenseBlob 是已编码的许可证文件 (.bin) 中许可证 blob 的整个字符串。 可以在所选的文本编辑器中打开已编码的许可证文件。

      $namespaceName = "root\cimv2\mdm\dmmap"
      $className = "MDM_EnterpriseModernAppManagement_StoreLicenses02_01"
      $methodName = "AddLicenseMethod"
      $parentID = "./Vendor/MSFT/EnterpriseModernAppManagement/AppLicenses/StoreLicenses"
      
      #Update $contentID with the ContentID value from the unencoded license file (.xml)
      $contentID = "{'ContentID'_in_unencoded_license_file}"
      
      #Update $licenseBlob with the entire String in the encoded license file (.bin)
      $licenseBlob = "{Entire_String_in_encoded_license_file}"
      
      $session = New-CimSession
      
      #The final string passed into the AddLicenseMethod should be of the form <License Content="encoded license blob" />
      $licenseString = '<License Content='+ '"' + $licenseBlob +'"' + ' />'
      
      $params = New-Object Microsoft.Management.Infrastructure.CimMethodParametersCollection
      $param = [Microsoft.Management.Infrastructure.CimMethodParameter]::Create("param",$licenseString ,"String", "In")
      $params.Add($param)
      
      try
      {
           $instance = New-CimInstance -Namespace $namespaceName -ClassName $className -Property @{ParentID=$parentID;InstanceID=$contentID}
           $session.InvokeMethod($namespaceName, $instance, $methodName, $params)
      }
      catch [Exception]
      {
           Write-Host $_ | Out-String
      }
      

演示脚本

你可以在 GitHub 存储库中找到测试 MSIX 包的所有四个阶段的演示脚本,以及有关如何使用它们的语法帮助。 这些脚本适用于任何版本的 PowerShell 和任何磁盘映像格式。

后续步骤

详细了解 Azure 虚拟桌面中的应用附加和 MSIX 应用附加: