如何在 Windows 8 和 10 中枚举已安装的 StoreApps 及其 ID

How to enumerate the installed StoreApps and their ID in Windows 8 and 10

我想得到的是所有安装的StoreApp应用的AppUserModelId,这样我就可以把它传递给IApplicationActivationManager->ActivateApplication

在 Windows 8 中它存储在注册表中,但在 Windows 10 中它不再存在。

互联网上有很多关于这个的问题,但即使经过几天的搜索,我也找不到令人满意的解决方案。

到目前为止我有以下内容:

  1. 我创建了一个实例 IPackageManager,
  2. 我用当前用户的SID调用FindPackagesByUserSecurityId(),
  3. 我遍历返回的集合
  4. 我得到一个IPackage接口
  5. 从那里我得到一个 IPackageId 接口,
  6. 然后我打电话给IPackageId->get_FamilyName()

例如,我在 Windows 10 上为 Windows 计算器输入字符串“Microsoft.WindowsCalculator_8wekyb3d8bbwe”。

当我向这个字符串附加一个“!App”时,我有完整的 AppUserModelId 来启动 Windows 计算器:“Microsoft.WindowsCalculator_8wekyb3d8bbwe!App

但并非所有应用程序都在 FamilyName 后面使用“!App”。 例如 Spartan 使用 AppUserModelId “Microsoft.Windows.Spartan_cw5n1h2txyewy!Microsoft.Spartan.Spartan”不以“!App”结尾。当我将“!Microsoft.Spartan.Spartan”替换为“!App”时,它不会启动 -> "This app does not support the contract specified".

所以我的问题是我从哪里得到最后缺失的部分?

我在互联网 (http://poshcode.org/5702) 上找到了一个 PowerShell 代码,它似乎做的事情非常相似:

Get-AppXPackage $PackageName -pv Package |
    Get-AppxPackageManifest | % {
        foreach($Application in $_.Package.Applications.Application) {
            if($Application.Id -like $AppId) {
                if($Protocol -and !($Application.Extensions.Extension.Protocol.Name | ? { ($_ + "://") -match (($Protocol -replace '\*','.*') + "(://)?") })) {
                    continue
                }

                [PSCustomObject]@{
                    # Notice the secret magic property:
                    PSTypeName = "Microsoft.Windows.Appx.Application"
                    AppUserModelId = $Package.PackageFamilyName + "!" + $Application.Id
                }
            }
        }
    }

我真的不明白这些神秘的 PowerShell 东西,但有一行对我来说似乎很有趣:

foreach($Application in $_.Package.Applications.Application)

这似乎是在一个包中枚举应用程序。

同一 PowerShell 代码中的注释说:

# The full AppUserModelId is composed of the package name, 
the publisher id, and the app id, such as
Microsoft.ZuneMusic_8wekyb3d8bbwe!Microsoft.ZuneMusic

所以缺少的是$Application.Id

如果我能得到一个 IAppInfo 接口,我可以调用 IAppInfo->get_Id(),我就准备好了。

但我不知道如何从 C++ 中的 IPackage 中获取它。

令人难以置信的是,没有人有想法! 这表明微软是如何让我们生活艰难的。 像使用 AppUserModelId 枚举已安装的 StoreApps 这样的通用任务需要一个科学研究部门。

我终于找到了一个在 Windows 8 和 Windows 10 上完美运行的解决方案。但是需要很多代码。

似乎 Windows 没有在内存中保存应用程序 ID,也没有 API 直接确定它们。我研究了 Windows 10 SDK 中的所有头文件,但找不到可用于该任务的相应接口。

但我找到了获取它们的方法。我在问题的 6 个步骤之后继续:

  1. 调用 IPackage->get_InstalledLocation() 其中 returns 一个 IStorageFolder
  2. IStorageItem
  3. 的查询接口
  4. 致电IStorageItem->get_Path()

现在您有了应用程序的安装路径。 Windows 10 使用两个基本文件夹:

  • C:\程序Files\WindowsApps
  • C:\Windows\SystemApps

和其他几个人

  • C:\Windows\vpnplugins
  • C:\Windows\devicesflow
  • C:\Windows\MicracastView
  • C:\Windows\PrintDialog
  • C:\Windows\PrintDialog3D
  • C:\Windows\WinStore

在返回的文件夹路径中,您将找到一个文件“AppxManifest.xml”。 这个文件看起来像:

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns=".....">
    ......
    ......
    <Applications>
        <Application Id="microsoft.windowslive.mail" Executable="HxMail.exe" EntryPoint="Executable">
        ......
        ......
        </Application>
        <Application Id="microsoft.windowslive.calendar" Executable="HxCalendarAppImm.exe" EntryPoint="Executable">
        ......
        ......
        </Application>
    </Applications>
</Package>

瞧,它们就在那里。这个包有两个应用程序 ID:“microsoft.windowslive.mail”和“microsoft.windowslive.calendar”。

然后从第 6 步中获取包的 FamilyName 附加一个“!”并附加此 ID,您就完成了。

可以使用 AppUserModelId 之一从 IApplicationActivationManager->ActivateApplication() 启动此包:

  • "microsoft.windowscommunicationsapps_8wekyb3d8bbwe!microsoft.windowslive.calendar"
  • "microsoft.windowscommunicationsapps_8wekyb3d8bbwe!microsoft.windowslive.mail"

使用 PackageManager API 枚举包,使用 GetPackageApplicationIds 枚举包中的应用程序,例如伪代码

FOREACH p IN PackageManager.FindPackagesForUserWithPackageTypes(null,
PackageType_Main|PackageType_Optional)
{
    PACKAGE_INFO_REFERENCE pir
    OpenPackageInfoByFullName(p.Id.FullName, 0, &pir)
    UINT32 n=0
    GetPackageApplicationIds(pir, &n, null, null)
    BYTE* buffer = new BYTE[n]
    UINT32 count=0
    GetPackageApplicationIds(pir, &n, buffer, &count)
    ClosePackageInfo(pir)
    PCWSTR * applicationUserModelIds = reinterpret_cast<PCWSTR*>(buffer);
    FOR (i=0; i<count; ++i)
    {
        PCWSTR applicationUserModelId = applicationUserModelIds[i]
    }
    delete [] buffer
}

请参阅 MSDN 上的 GetPackageApplicationIds() 了解更多详细信息,包括工作示例代码 https://msdn.microsoft.com/en-us/library/windows/desktop/dn270603(v=vs.85).aspx