Im trying to enumerate all files on a given path and also get their properties.
On my implementation below, i dont understand whats happening, it looks like it somehow is using the computer desktop
path instead of the path on folderPath
at hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &displayName);
it shows:
C:\Users\Lilo\Desktop\file.txt
instead of: C:\Users\Lilo\Documents\file.txt
file.txt is on the documents folder not on the desktop
and consequently both SHGetPropertyStoreFromIDList
and SHGetPropertyStoreFromParsingName
fails with
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : The system cannot find the file specified.
#include <windows.h>
#include <iostream>
#include <shlobj.h>
#include <atlcomcli.h>
inline void enumFiles(const std::wstring& folderPath)
{
CoInitialize(NULL);
HRESULT hr;
CComPtr<IShellItem> pFolderItem = NULL;
CComPtr<IShellFolder> pFolder = NULL;
hr = SHCreateItemFromParsingName(folderPath.c_str(), NULL, IID_PPV_ARGS(&pFolderItem));
if (FAILED(hr))
{
std::cout << "SHCreateItemFromParsingName failed with HRESULT:" << hr;
CoUninitialize();
return;
}
hr = pFolderItem->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARGS(&pFolder));
if (FAILED(hr))
{
std::cout << "BindToHandler failed with HRESULT:" << hr;
CoUninitialize();
return;
}
CComPtr<IEnumIDList> pEnum = NULL;
hr = pFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnum);
if (FAILED(hr))
{
std::cout << "EnumObjects failed with HRESULT:" << hr;
CoUninitialize();
return;
}
ITEMIDLIST* pidl = NULL;
ULONG fetched;
while (pEnum->Next(1, &pidl, &fetched) == S_OK)
{
CComPtr<IShellItem> pItem;
hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&pItem));
if (FAILED(hr))
{
std::cout << "SHCreateItemFromIDList failed with HRESULT:" << hr;
CoTaskMemFree(pidl);
continue;
}
LPWSTR displayName;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &displayName);
if (SUCCEEDED(hr))
CoTaskMemFree(displayName);
CComPtr<IPropertyStore> pPropertyStore;
GETPROPERTYSTOREFLAGS flags = GPS_DEFAULT;
hr = SHGetPropertyStoreFromIDList(pidl, flags, IID_PPV_ARGS(&pPropertyStore));
//hr = SHGetPropertyStoreFromParsingName(displayName, NULL, GPS_DEFAULT, IID_PPV_ARGS(&pPropertyStore));
CoTaskMemFree(pidl);
if (FAILED(hr))
{
std::cout << "SHGetPropertyStoreFromIDList failed with HRESULT:" << hr;
continue;
}
}
CoUninitialize();
}
int main(int argc, char* argv[])
{
enumFiles(L"C:\\Users\\Lilo\\Documents");
}
You can keep on using IShellItem
and friends (IEnumShellItems) all along, IShellFolder
is the underlying folder interface that's not easy to use, something like this (error-checks ommited):
void enumFiles(const std::wstring& folderPath)
{
CComPtr<IShellItem> folder;
SHCreateItemFromParsingName(folderPath.c_str(), nullptr, IID_PPV_ARGS(&folder));
// create a bind context to define what we want to enumerate
CComPtr<IPropertyBag> bag;
PSCreateMemoryPropertyStore(IID_PPV_ARGS(&bag));
CComPtr<IBindCtx> ctx;
CreateBindCtx(0, &ctx);
ctx->RegisterObjectParam((LPOLESTR)STR_PROPERTYBAG_PARAM, bag);
auto flags = CComVariant(SHCONTF_FOLDERS | SHCONTF_NONFOLDERS);
bag->Write(STR_ENUM_ITEMS_FLAGS, &flags);
CComPtr<IEnumShellItems> items;
folder->BindToHandler(ctx, BHID_EnumItems, IID_PPV_ARGS(&items));
do
{
CComPtr<IShellItem> item;
if (items->Next(1, &item, nullptr) != S_OK)
break;
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_FILESYSPATH, &displayName);
std::wcout << displayName.m_pData << std::endl;
CComPtr<IPropertyStore> store;
if (FAILED(item->BindToHandler(nullptr, BHID_PropertyStore, IID_PPV_ARGS(&store))))
{
CComPtr<IPropertyStoreFactory> factory;
item->BindToHandler(nullptr, BHID_PropertyStore, IID_PPV_ARGS(&factory));
factory->GetPropertyStore(GPS_BESTEFFORT, nullptr, IID_PPV_ARGS(&store));
}
// etc.
} while (true);
}
int main(int argc, char* argv[])
{
CoInitialize(NULL);
{
enumFiles(L"C:\\Users\\Lilo\\Documents");
}
CoUninitialize();
return 0;
}
See here for more on the possible bind contexts.
PS 1: don't call CoInitalize
in a library code, just call it once per process/thread.
PS 2: ATL has CComHeapPtr
for smart memory pointer.