3

I want to get a list of applications that are installed and executable on Windows 10 -- i.e. executables of applications a user can launch (UWP and non-UWP). For UWP apps, I want to get its AppUserModelId. For non-UWP apps, I want to get the executable's file location.

So far, I have tried: 1) Going through the registry and iterate through the installed applications. However, this method goes through all installed applications, the returned list would include things like microsoft asp.net core 2.1.8 shared framework (x64), which I want to eliminate.

2) I found that I can access what I wanted through the special Appsfolder, by typing shell:Appsfolder into either the explorer window or Run. So far I was able to get access to this folder, and iterate through the items and list the names of the files. But I am not sure how to collect information on AppUserModelId (for UWP apps) and filepath (for WPF apps).

Relevant code I used to enumerate Appsfolder files:

namespace AppsFolder
{
    ...
    public static class AppsFolder
    {
       ...

       // GUID for shell:Appsfolder
       public static readonly Guid KnownFolderID = new Guid("1e87508d-89c2-42f0-8a7e-645a0f50ca58");

       public static IEnumerable<string> GetItemNames(ESHGDN gdn)
       {
           IShellFolder appsFolder = GetAppsFolder();
           try
           {
               IntPtr enumIDListInterfacePointer;
               appsFolder.EnumObjects(IntPtr.Zero,
                    ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS,
                   out enumIDListInterfacePointer);
               using (var enumIDList = new
                      EnumIDListWrapper(enumIDListInterfacePointer))
               {
                  var names = new List<string>();
                  foreach (var pidl in enumIDList)
                  {
                      STRRET name;
                      recycleBin.GetDisplayNameOf(
                              pidl.DangerousGetHandle(),
                             gdn,
                             out name);
                      names.Add(STRRETToString(ref name, pidl));
                      if (name.uType != (int)STRRETType.Offset)
                         Marshal.FreeCoTaskMem(name.data.pOleStr);
                  }
                  return names;
               }
           }
           finally
           {
               Marshal.FinalReleaseComObject(recycleBin);
           }
        }

        private static IShellFolder GetAppsFolder()
        {
            IShellFolder desktop;
            NativeMethod.SHGetDesktopFolder(out desktop);
            try
            {
                Guid shellFolderInterfaceId = typeof(IShellFolder).GUID;
                IntPtr recycleBinShellFolderInterfacePointer;
                desktop.BindToObject(
                    GetItemIDList().DangerousGetHandle(),
                    IntPtr.Zero,
                    ref shellFolderInterfaceId,
                    out recycleBinShellFolderInterfacePointer);
                return (IShellFolder)Marshal.GetObjectForIUnknown(
                             recycleBinShellFolderInterfacePointer);
            }
            finally
            {
                Marshal.FinalReleaseComObject(desktop);
            }
         }

         public static SafeCoTaskMemHandleZeroIsInvalid GetItemIDList()
         {
             SafeCoTaskMemHandleZeroIsInvalid pidl;
             Guid guid = KnownFolderID;
             NativeMethod.SHGetKnownFolderIDList(
                ref guid, 0, IntPtr.Zero, out pidl);
             return pidl;
         }
     }
}

Executed as:

string[] names = AppsFolder.GetItemNames(AppsFolder.ESHGDN.SHGDN_INFOLDER).ToArray();
for (int i=0; i<names.Length; i++)
{
    Console.WriteLine(names[i]);
}

The ILaunchSourceAppUserModelID interface provides a method to get the AppUserModelID, but I'm not sure how to get access to this interface through my IShellFolder interface. Also, how do I get the actual file location within the AppsFolder virtual folder to actual exe's filepath?

Asy
  • 313
  • 1
  • 3
  • 11
  • What you describe only works for apps that are registered with the OS, but that is not a requirement. Apps can be installed and be executable without being registered with the OS. If you really want to find *everything*, you will likely end up having to enumerate the whole file system. – Remy Lebeau May 23 '19 at 01:50
  • Right, I understand that. At this point I just want to find those that are registered with the OS. Can you recommend a better way to do this? – Asy May 23 '19 at 07:30

4 Answers4

7

Check out this answer.

In short, add the Microsoft.WindowsAPICodePack-Shell nuget package to your project and then use this code:

// GUID taken from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FODLERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FODLERID_AppsFolder);

foreach (var app in (IKnownFolder)appsFolder)
{
    // The friendly app name
    string name = app.Name;
    // The ParsingName property is the AppUserModelID
    string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
    // You can even get the Jumbo icon in one shot
    ImageSource icon =  app.Thumbnail.ExtraLargeBitmapSource;
}

To start the app:

System.Diagnostics.Process.Start("explorer.exe", @" shell:appsFolder\" + appUserModelID);

Works for both regular apps and store apps.

user1969903
  • 810
  • 13
  • 26
  • The provided package is not maintained anymore. Here is the new one with support for .NET 6: https://www.nuget.org/packages/Microsoft-WindowsAPICodePack-Shell/ – Damian Ubowski Jul 29 '23 at 11:29
  • Also, launching the app with the Process.Start() method doesn't work when there is a GUID in a AppUserModelID (ex. {F38BF404-1D43-42F2-9305-67DE0B28FC23}\regedit.exe). It just starts the default Documents folder – Damian Ubowski Jul 31 '23 at 10:19
  • @DamianUbowski I'm working on my own code to read the contents of the virtual folder so that I can get rid of WindowsAPICodePack-Shell completely. As for the other topic, you should be starting it like this `Process.Start("explorer.exe", @"shell:appsFolder\{F38BF404-1D43-42F2-9305-67DE0B28FC23}\regedit.exe");` Tested just now and works fine. – user1969903 Aug 02 '23 at 11:00
0

You can get Application User Model ID (AUMID) for all apps installed for the current user by running the following command in Windows PowerShell command prompt:

get-StartApps

To get AUMIDs for Windows Store apps installed for another user, run the following command in Windows PowerShell command prompt:

$installedapps = get-AppxPackage

$aumidList = @()
foreach ($app in $installedapps)
{
    foreach ($id in (Get-AppxPackageManifest $app).package.applications.application.id)
    {
        $aumidList += $app.packagefamilyname + "!" + $id
    }
}

$aumidList

More detailed information you can refer to "Find the Application User Model ID of an installed app".

What you need to do is call related registry API or Powershell command from C#.

Rita Han
  • 9,574
  • 1
  • 11
  • 24
0

You get the AppUserModelId with IShellFolder2.GetDetailsEx and PKEY_AppUserModel_ID

(tested in C# on Windows 10)

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PROPERTYKEY
{
    private readonly Guid _fmtid;
    private readonly uint _pid;

    public PROPERTYKEY(Guid fmtid, uint pid)
    {
        _fmtid = fmtid;
        _pid = pid;
    }

    public static readonly PROPERTYKEY PKEY_AppUserModel_ID = new PROPERTYKEY(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 5);     
}
Castorix
  • 1,465
  • 1
  • 9
  • 11
0

I have a solution, you don't need to add any extra dll or namespace to get the app names from window app store:

            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            p.StartInfo.FileName = "powershell.exe";
            p.StartInfo.Arguments = @"Get-AppxPackage | select name";
            p.Start();
            output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();

all applications list included in the output.

Sun Tao
  • 24
  • 1