user3256556
user3256556

Reputation: 173

Listing of installed apps on Windows 10

I am trying to programmatically list all the installed app on my win10 system.
Basically, I am trying to get the list that you can see when you type "shell:appsFolder" in an explorer window.

Here is the code that I use:

HRESULT hr;
IShellFolder *psParent= nullptr, *psApps= nullptr;
LPITEMIDLIST pidlSystem = NULL;
hr = SHGetFolderLocation(NULL, CSIDL_SYSTEM, NULL, NULL, &pidlSystem); // get root pidl which is needed to get the parrent of our app folder
LPITEMIDLIST pidlApps= NULL;

if (!SUCCEEDED(hr= SHGetKnownFolderItem(FOLDERID_AppsFolder, KF_FLAG_DEFAULT, NULL, IID_IShellItem , (void**)&pidlApps)))
    goto done;  // get pidl for apps folder

if (!SUCCEEDED(hr=SHBindToParent(pidlSystem, IID_IShellFolder, (void **) &psParent, (LPCITEMIDLIST*)&pidlApps)))
    goto done; // Get shell folder of parrent, which is needed to get shell folder of appsFolder

psApps= NULL;

if (!SUCCEEDED(hr= psParent->BindToObject(pidlApps, nullptr, IID_IShellFolder, (void **)&psApps)))
    goto done; // finally get shell folder of appsFolder!

IEnumIDList *IDList;

if (!SUCCEEDED(hr= psApps->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &IDList)))
    goto done; // start the file scanning process

LPITEMIDLIST object= (LPITEMIDLIST)CoTaskMemAlloc(sizeof(*object)); // allocate room to receive data..

while (IDList->Next(1, &object, nullptr)!=S_FALSE)                  // and the list loop
{
    STRRET strDispName;
    if (!SUCCEEDED(hr= psApps->GetDisplayNameOf(object, SHGDN_NORMAL, &strDispName)))
        continue; // get the name in some weired format
    TCHAR szDisplayName[MAX_PATH];
    if (!SUCCEEDED(hr= StrRetToBuf(&strDispName, pidlApps, szDisplayName, sizeof(szDisplayName))))
        continue; // transform it to string...
    ATLTRACE2(L"found %s\n", szDisplayName);
}
IDList->Release();
CoTaskMemFree(object);

The problem is that I end up with a list of over 2000 items, including .jpg and other .dll files, but this list does NOT correspond to what the explorer window shows me! For example, "word" is not there, neither as a link, nor as a winword.exe, while is is in the explorer window.

Any clue what is going on?

Upvotes: 2

Views: 1543

Answers (2)

Simon Mourier
Simon Mourier

Reputation: 139158

If shell:appsFolder is really what you want, then use it. Something like this:

CoInitialize(NULL);
CComPtr<IShellItem> folder;
if (SUCCEEDED(SHCreateItemFromParsingName(L"shell:appsFolder", nullptr, IID_PPV_ARGS(&folder))))
{
  CComPtr<IEnumShellItems> enumItems;
  if (SUCCEEDED(folder->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&enumItems))))
  {
    IShellItem* items;
    while (enumItems->Next(1, &items, nullptr) == S_OK)
    {
      CComPtr<IShellItem> item = items;
      CComHeapPtr<wchar_t> name;
      if (SUCCEEDED(item->GetDisplayName(SIGDN_NORMALDISPLAY, &name)))
      {
        wprintf(L"%s\n", name);
      }

      // dump all properties
      CComPtr<IPropertyStore> store;
      if (SUCCEEDED(item->BindToHandler(NULL, BHID_PropertyStore, IID_PPV_ARGS(&store))))
      {
        DWORD count = 0;
        store->GetCount(&count);
        for (DWORD i = 0; i < count; i++) {
          PROPERTYKEY pk;
          if (SUCCEEDED(store->GetAt(i, &pk)))
          {
            CComHeapPtr<wchar_t> pkName;
            PSGetNameFromPropertyKey(pk, &pkName); // needs propsys.lib

            PROPVARIANT pv;
            PropVariantInit(&pv);
            if (SUCCEEDED(store->GetValue(pk, &pv)))
            {
              CComHeapPtr<wchar_t> pvs;
              pvs.Allocate(512);
              PropVariantToString(pv, pvs, 512); // needs propvarutil.h and propsys.lib
              PropVariantClear(&pv);
              wprintf(L" %s=%s\n", pkName, pvs);
            }
            else
            {
              wprintf(L" %s=???\n", pkName);
            }
          }
        }
      }
    }
  }
}

CoUninitialize();

note1: I've used Visual Studio's ATL smart classes, which is much easier.

note2: I've used IShellItem which is somehow the new way to program the Shell. You shouldn't need to use IShellFolder, IEnumIDList, STRRET, etc anymore

Note3: I've added a code to dump all properties of an item. The list an content of properties depends on the type of application. Here are tow examples:

Google Chrome (not a UWP app)
 System.ItemNameDisplay=Google Chrome
 System.Keywords=chrome
 System.Tile.Background=4285031263
 System.Tile.Foreground=4294967295
 System.Tile.Square150x150LogoPath=79.0.3945.130\VisualElements\Logo.png
 System.Tile.Flags=1185
 System.Tile.Square70x70LogoPath=79.0.3945.130\VisualElements\SmallLogo.png
 System.ThumbnailCacheId=15284856079963922257
 System.Link.TargetParsingPath=C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
 System.AppUserModel.ID=Chrome
 System.AppUserModel.PreventPinning=0
 System.AppUserModel.BestShortcut=20; 0; 31; 80; 224; 79; 208; 32; 234; 58; 105; 16; 162; 216; 8; 0; 43; 48; 48; 157; 25; 0; 47; 67; 58; 92; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 98; 0; 49; 0; 0; 0; 0; 0; 0; 0; 0; 0; 16; 0; 80; 114; 111; 103; 114; 97; 109; 68; 97; 116; 97; 0; 72; 0; 9; 0; 4; 0; 239; 190; 0; 0; 0; 0; 0; 0; 0; 0; 46; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 80; 0; 114; 0; 111; 0; 103; 0; 114; 0; 97; 0; 109; 0; 68; 0; 97; 0; 116; 0; 97; 0; 0; 0; 26; 0; 92; 0; 49
 System.AppUserModel.HostEnvironment=0
 System.AppUserModel.InstalledBy=0
 System.AppUserModel.RunFlags=1
 ...

Paint 3D (a UWP app).
 System.ItemNameDisplay=Paint 3D
 System.Tile.SmallLogoPath=Assets\Logos\Square44x44\PaintAppList.png
 System.Tile.Background=4292311040
 System.Tile.Foreground=4294967295
 System.Tile.LongDisplayName=Paint 3D
 System.Tile.Square150x150LogoPath=Assets\Logos\Square150x150\PaintMedTile.png
 System.Tile.Wide310x150LogoPath=Assets\Logos\Wide310x150\PaintWideTile.png
 System.Tile.Flags=1185
 System.Tile.Square310x310LogoPath=Assets\Logos\Square310x310\PaintLargeTile.png
 System.Tile.Square70x70LogoPath=Assets\Logos\Square71x71\PaintSmallTile.png
 System.Launcher.AppState=0
 System.ThumbnailCacheId=10531553521183290931
 System.AppUserModel.ID=Microsoft.MSPaint_8wekyb3d8bbwe!Microsoft.MSPaint
 System.AppUserModel.HostEnvironment=1
 System.AppUserModel.PackageInstallPath=C:\Program Files\WindowsApps\Microsoft.MSPaint_6.1907.18017.0_x64__8wekyb3d8bbwe
 System.AppUserModel.PackageFamilyName=Microsoft.MSPaint_8wekyb3d8bbwe
 System.AppUserModel.PackageFullName=Microsoft.MSPaint_6.1907.18017.0_x64__8wekyb3d8bbwe
 System.AppUserModel.TileUniqueId={3911A337-8941-4141-9D5D-6C2575D40995}

Upvotes: 5

Remy Lebeau
Remy Lebeau

Reputation: 597315

Your code is needlessly complicated, and very buggy.

SHGetKnownFolderItem() does not output a PIDL like you have coded, it outputs an IShellItem instead, because that is what you are asking it for.

You don't need pidlSystem at all, especially since you are not using it correctly anyway. The way you are using SHBindToParent(), your code is getting the IShellFolder of the Windows System folder, not the Apps folder. You are enumerate the contents of the System folder, which is why you do not see the Apps you are expecting.

Once you have the IShellItem for the Apps folder from SHGetKnownFolderItem(), you can enumerate its child items directly, using IShellItem::BindToHandler(). You don't need to use IShellFolder at all.

Also, your code is full of memory leaks.

Try something more like this instead:

IShellItem *pApps = nullptr;
HRESULT hr = SHGetKnownFolderItem(FOLDERID_AppsFolder, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&pApps)); // get apps folder
if (FAILED(hr)) goto done;

IEnumShellItems *ItemList = nullptr;
hr = pApps->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&ItemList)); // start the file scanning process
if (FAILED(hr)) {
    pApps->Release();
    goto done;
}

IShellItem *Item = nullptr;
while (ItemList->Next(1, &Item, nullptr) == S_OK) // and the list loop
{
    LPWSTR pDisplayName = nullptr;
    hr = Item->GetDisplayName(SIGDN_NORMALDISPLAY, &pDisplayName); // get the name
    if (SUCCEEDED(hr)) {
        ATLTRACE2(L"found %s\n", pDisplayName);
        CoTaskMemFree(pDisplayName);
    }
    Item->Release();
}

ItemList->Release();
pApps->Release();

Upvotes: 2

Related Questions