Elmue
Elmue

Reputation: 8138

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

What I want to get is the AppUserModelId of all installed StoreApp applications, so that I can pass it to IApplicationActivationManager->ActivateApplication.

In Windows 8 it was stored in the Registry, but in Windows 10 it is not anymore.

There are a lot of questions about this in internet but even after days of searching I could not find a satisfying solution.

What I have so far is the following:

  1. I create an instance of IPackageManager,
  2. I call FindPackagesByUserSecurityId() with the SID of the current user,
  3. I iterate through the returned collection
  4. I get an IPackage interface
  5. From that I get an IPackageId interface,
  6. Then I call IPackageId->get_FamilyName()

With that I have for example on Windows 10 for the Windows Calculator the string "Microsoft.WindowsCalculator_8wekyb3d8bbwe".

When I append to this string an "!App" I have the complete AppUserModelId to start the Windows Calculator: "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"

But not all applications use an "!App" behind the FamilyName. For example Spartan uses the AppUserModelId "Microsoft.Windows.Spartan_cw5n1h2txyewy!Microsoft.Spartan.Spartan" which does not end with "!App". And when I replace "!Microsoft.Spartan.Spartan" with "!App" it will not start -> "This app does not support the contract specified".

So my question is where do I get the last missing part from?

I found a PowerShell code in internet (http://poshcode.org/5702) that seems to do something very similar:

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
                }
            }
        }
    }

I really don't understand this cryptic PowerShell stuff, but one line seems interesting to me:

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

This seems to enumerate Applications in a Package.

A comment in the same PowerShell code says:

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

so what is missing is the $Application.Id.

If I could get an IAppInfo interface anyhow I could call IAppInfo->get_Id() and I would be ready.

But I don't know how to get this from an IPackage in C++.

Upvotes: 2

Views: 4004

Answers (2)

Elmue
Elmue

Reputation: 8138

Incredible that nobody has an idea! This shows how Microsoft makes us life hard. Such a universal task like enumerating the installed StoreApps with their AppUserModelId requires a cientific research department.

I finally came to a solution that works perfectly on Windows 8 and Windows 10. But a lot of code is required.

It seems that Windows does not hold the Application ID's in memory and there is no API to determine them directly. I studied all header files in the Windows 10 SDK and could not find a corresponding interface useful for that task.

But I found out how to get them. I continue after the 6 steps in my question:

  1. call IPackage->get_InstalledLocation() which returns an IStorageFolder.
  2. QueryInterface for IStorageItem
  3. call IStorageItem->get_Path()

Now you have the path were the App is installed. Windows 10 uses two base folders:

  • C:\Program Files\WindowsApps
  • C:\Windows\SystemApps

and several others like

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

In the returned folder path you will find a file "AppxManifest.xml". This file looks like:

<?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>

And voilà, there they are. This package has two application ID's: "microsoft.windowslive.mail" and "microsoft.windowslive.calendar".

Then you take the package's FamilyName from step 6 append an "!" and append this ID and you are done.

This package can be started with IApplicationActivationManager->ActivateApplication() using one of the AppUserModelId's:

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

Upvotes: 4

Howard Kapustein
Howard Kapustein

Reputation: 527

Use PackageManager APIs to enumerate packages and GetPackageApplicationIds to enumerate applications in a package e.g. pseudo-code

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
}

See GetPackageApplicationIds() on MSDN for more details including working sample code https://msdn.microsoft.com/en-us/library/windows/desktop/dn270603(v=vs.85).aspx

Upvotes: 1

Related Questions