如何将OpenSavePidlMRU字节转换为ITEMIDLIST并提取注册表路径?
从OpenSavePidlMRU注册表项提取路径:字节转ITEMIDLIST的方法
注册表项HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU下的二进制值存储的是序列化的ITEMIDLIST(PIDL)原始字节流,无需复杂转换,直接将字节缓冲区指针强制类型转换为LPCITEMIDLIST即可,之后就能用Shell API转换为文件路径。
具体实现步骤
读取注册表中的PIDL字节数据
目标键下的子项或值名称通常对应文件扩展名(如*表示所有类型,.docx对应Word文档),通过RegQueryValueEx读取其二进制数据,得到PIDL的原始字节缓冲区。强制转换为ITEMIDLIST指针
注册表中存储的字节流完全符合ITEMIDLIST的内存结构(由多个SHITEMID串联而成,每个SHITEMID的cb字段标识自身长度),直接将缓冲区指针转为LPCITEMIDLIST即可。转换为本地文件路径
推荐使用SHGetPathFromIDListEx(支持长路径,兼容性更好)替代GetPathFromIDList,传入转换后的PIDL即可获取对应的本地路径。
C++ 代码示例
#include <windows.h> #include <shlobj_core.h> #include <iostream> #include <vector> #pragma comment(lib, "shell32.lib") #pragma comment(lib, "advapi32.lib") int main() { HKEY hKey; LSTATUS status = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU", 0, KEY_READ, &hKey); if (status != ERROR_SUCCESS) { std::cerr << "Failed to open registry key: " << status << std::endl; return 1; } // 读取"*"对应的值(代表所有文件类型的最近路径) DWORD dataSize = 0; status = RegQueryValueExW(hKey, L"*", nullptr, nullptr, nullptr, &dataSize); if (status != ERROR_SUCCESS) { std::cerr << "Failed to query value size: " << status << std::endl; RegCloseKey(hKey); return 1; } std::vector<BYTE> pidlBytes(dataSize); status = RegQueryValueExW(hKey, L"*", nullptr, nullptr, pidlBytes.data(), &dataSize); if (status != ERROR_SUCCESS) { std::cerr << "Failed to read value data: " << status << std::endl; RegCloseKey(hKey); return 1; } RegCloseKey(hKey); // 将字节缓冲区转为ITEMIDLIST指针 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(pidlBytes.data()); // 转换为本地路径 WCHAR path[MAX_PATH] = {0}; if (SHGetPathFromIDListEx(pidl, path, MAX_PATH, GPFIDL_DEFAULT)) { std::wcout << "Extracted path: " << path << std::endl; } else { std::cerr << "Failed to convert PIDL to path. This may be a non-local PIDL." << std::endl; // 可选:获取显示名称 WCHAR displayName[MAX_PATH] = {0}; if (SHGetNameFromIDList(pidl, SIGDN_DESKTOPABSOLUTEPARSING, displayName, MAX_PATH) == S_OK) { std::wcout << "Display name: " << displayName << std::endl; } } return 0; }
注意事项
- 若PIDL对应非本地文件系统(如网络共享、云存储),
SHGetPathFromIDListEx会失败,此时可使用SHGetNameFromIDList获取显示名称,或通过BindToObject进一步处理对象; - 读取的字节缓冲区需确保有效,无效PIDL会导致API调用失败;
- 无需手动释放PIDL内存,只需释放读取字节的缓冲区即可。
内容的提问来源于stack exchange,提问作者Jerome
相关产品推荐
相关产品推荐

