测试应用附加的 MSIX 包

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

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

先决条件

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

不需要 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. 通过运行以下命令监视应用程序包的暂存进度。 暂存包所需的时间取决于其大小。 转移完成后,Status 变量的 $stagingResult 属性将为 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 包。

为 App Attach 代理设置模拟脚本

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

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

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

注意

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

脱机使用包

如果在未连接到 Internet 的设备上使用包,则需要确保设备上安装了包许可证以便成功运行应用。 如果设备处于联机状态,则所需的许可证应会自动下载。

若要安装许可证文件,需要使用 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 虚拟桌面中的 App Attach 功能的更多信息: