C++调用Windows Shell API遍历文件夹时路径始终返回桌面问题
代码问题根因
核心错误是给SHGetPathFromIDListW传入了相对PIDL(Shell项标识符列表):
- 你通过枚举
pEnumIDList拿到的pChild是相对PIDL,仅保存了子项相对于当前绑定的父Shell文件夹的标识信息,不包含父文件夹本身的完整路径链。 SHGetPathFromIDListW仅接受从Shell命名空间根(桌面)开始的绝对PIDL,传入相对PIDL时,API无法回溯完整父路径,会默认以桌面作为父路径拼接文件名,就会出现文件名正确、父路径永远显示为桌面的异常。- 原代码还存在资源泄漏问题:枚举得到的
pChild、枚举接口pEnumIDList使用完后没有释放,长期运行会持续占用内存和COM对象资源。
修复方案
需要先把父文件夹的绝对PIDL和子项的相对PIDL拼接为完整的子项绝对PIDL,再传入路径解析接口,同时补全所有资源释放逻辑。核心修改代码如下:
// 需提前引入头文件 <shlobj_core.h>,链接时添加shell32.lib依赖 bool ListFolderContent(const std::wstring& fileName) { PIDLIST_ABSOLUTE pidl = nullptr; if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr))) return false; IShellFolder* pShellfolder = nullptr; LPCITEMIDLIST pidlRelative = nullptr; HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative); if (FAILED(hr)) { ::CoTaskMemFree(pidl); return false; } IEnumIDList* pEnumIDList = nullptr; hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnumIDList); if (FAILED(hr)) { pShellfolder->Release(); ::CoTaskMemFree(pidl); return false; } LPITEMIDLIST pFullChildPidl = nullptr; while (true) { LPITEMIDLIST pChild = nullptr; hr = pEnumIDList->Next(1, &pChild, nullptr); if (FAILED(hr)) { pEnumIDList->Release(); pShellfolder->Release(); ::CoTaskMemFree(pidl); return false; } if (hr == S_FALSE) break; // 拼接父级绝对PIDL与子项相对PIDL,得到子项绝对PIDL pFullChildPidl = ILCombine(pidl, pChild); wchar_t buffer[MAX_PATH + 1]; if (pFullChildPidl && ::SHGetPathFromIDListW(pFullChildPidl, buffer)) { ::OutputDebugString(buffer); ::OutputDebugString(L"\r\n"); } // 释放当前循环内申请的PIDL资源 if (pFullChildPidl) { ::CoTaskMemFree(pFullChildPidl); pFullChildPidl = nullptr; } ::CoTaskMemFree(pChild); } // 统一释放所有剩余资源 pEnumIDList->Release(); pShellfolder->Release(); ::CoTaskMemFree(pidl); return true; }
内容的提问来源于stack exchange,提问作者Jean-Milost Reymond
相关产品推荐
相关产品推荐

