Reputation: 173
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
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
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