我试图通过一个打开的文件对话框获得最后一个打开的目录,似乎我们可以通过首先使用MRUListEx键的第一个字节检索包含路径的键名,然后通过读取这个键名的值来获得路径。MRUListEx键可以在:HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU上找到。问题是我不知道如何正确地填充"SHITEMID“结构。(它不允许我访问索引,它会引发内存访问冲突)。我甚至不知道下面的代码在任何一点上是否有效。
对不起,目前代码非常脏,但是当我最终找到导致这些错误的原因时,我会修改它。
void MyClass::OnDocumentSave(bool showSaveDlg)
{
// Do something that is not relevant here...
try {
HKEY hKey = NULL;
std::wstring regPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
throw std::exception(std::string("Unable to open the specified registry key, sys err code: ") + std::to_string(statusCode));
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// Please note that the buffer has intentionally a fixed size here and everything is
// simplified for readability, but it uses dynamic memory allocation in the real code to
// handle errors such as ERROR_MORE_DATA
BYTE* pathData[512];
bufferSize = 512;
DWORD type = 0; // In case it matters, the returned value is 3
statusCode = RegGetValueW(hKey, L"", std::to_wstring(data[0]).c_str(), RRF_RT_REG_BINARY, &type, &pathData, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// I don't know how to fill this structure, the documentation is very minimal,
// and I don't understand it.
int offset = sizeof(APPNAME);
SHITEMID shellIDList[2]{
// Throw a memory access violation at 0x*****, the debugger can't seems to
// get anything in pathData.
{ sizeof(USHORT) + sizeof(pathData), *pathData[0 + offset] },
{ 0, 0 } };
ITEMIDLIST idl{ shellIDList[0] };
// This is supposed give me the last path that was opened by a File Picker.
SHGetPathFromIDListW(&idl, initialDir.data());
}
catch (std::exception& e) {
// Silently set the initial directory to a hard-coded path instead of getting the registry value.
}
}发布于 2022-08-02 13:35:47
您似乎不明白类型和简单数组是如何工作的!BYTE* pathData[512];不是一个512字节的缓冲区。使用BYTE pathData[512];。
读入pathData后,打电话给SHGetPathFromIDList((PCIDLIST_ABSOLUTE) pathData, ...);。
尽管如此,ComDlg32键是没有文档的,我不认为它存储一个pidl,所以即使您的代码被更正了,它也不会工作。
EnumMRUListW是一个文档化的函数,您可以调用它,但它不会帮助您解码数据。
在我看来,这个位置可能以.exe的名称作为前缀,所以在找到真正的数据之前,至少要跳过(lstrlenW(pathData)+1)*2字节.
发布于 2022-08-02 14:00:47
如果您要使用无文档的东西,下面的代码可以转储像LastVisitedPidlMRU列表那样的MRU列表:
// the Shell object that can read MRU lists
static GUID CLSID_MruLongList = { 0x53bd6b4e,0x3780,0x4693,{0xaf,0xc3,0x71,0x61,0xc2,0xf3,0xee,0x9c} };
typedef enum MRULISTF
{
MRULISTF_USE_MEMCMP = 0x0,
MRULISTF_USE_STRCMPIW = 0x1,
MRULISTF_USE_STRCMPW = 0x2,
MRULISTF_USE_ILISEQUAL = 0x3,
};
typedef int (__stdcall *MRUDATALISTCOMPARE)(const BYTE*, const BYTE*, int);
MIDL_INTERFACE("00000000-0000-0000-0000-000000000000") // unknown guid but we don't care
IMruDataCompare : public IUnknown
{
public:
virtual HRESULT CompareItems(const BYTE*, int, const BYTE*, int) = 0;
};
MIDL_INTERFACE("d2c22919-91f5-4284-8807-58a2d64e561c")
IMruDataList2 : public IUnknown
{
public:
virtual HRESULT InitData(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, MRUDATALISTCOMPARE pfnCompare) = 0;
virtual HRESULT AddData(const BYTE* pData, DWORD cbData, DWORD* pdwSlot) = 0;
virtual HRESULT InsertData(const BYTE*, DWORD cbData, int* piIndex, DWORD* pdwSlot) = 0;
virtual HRESULT FindData(const BYTE* pData, DWORD cbData, int* piIndex) = 0;
virtual HRESULT GetData(int iIndex, BYTE* pData, DWORD cbData) = 0;
virtual HRESULT QueryInfo(int iIndex, DWORD* pdwSlot, DWORD* pcbData) = 0;
virtual HRESULT Delete(int iIndex) = 0;
virtual HRESULT InitData2(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, IMruDataCompare* pfnCompare) = 0;
};
int main()
{
CoInitialize(NULL);
{
CComPtr<IMruDataList2> mru;
if (SUCCEEDED(mru.CoCreateInstance(CLSID_MruLongList)))
{
const int max = 100; // get max 100 entries
mru->InitData(max, MRULISTF_USE_MEMCMP, HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU\\", nullptr);
for (auto i = 0; i < max; i++)
{
DWORD slot;
DWORD size;
// get size
if (FAILED(mru->QueryInfo(i, &slot, &size)))
continue;
// get data
// note beginning is a LPWSTR containing exe data
auto data = (LPBYTE)_alloca(size);
if (FAILED(mru->GetData(i, data, size)))
continue;
// the rest is a PIDL
auto pidl = (LPCITEMIDLIST)(data + (lstrlen((LPWSTR)data) + 1) * 2);
// get the shell item
CComPtr<IShellItem> item;
if (SUCCEEDED(SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&item))))
{
// get its path
CComHeapPtr<WCHAR> path;
item->GetDisplayName(SIGDN::SIGDN_DESKTOPABSOLUTEPARSING, &path);
wprintf(L"Executable: %s LastVisited: %s\n", data, path);
}
}
}
}
CoUninitialize();
}PS:我在用ATL的智能类
发布于 2022-08-03 11:29:56
我终于想出了如何创建一个ITEMIDLIST结构,这一切都很好。
解释
经过一些测试后,我得出的结论是,这个值不一定要更新。使用文件选择器的应用程序需要写入注册表以反映更改。例如,VS Community2022/ VS代码更新此值,但新的Microsoft记事本不更新(旧记事本不确定)。显然,.NET应用程序似乎是自动更新的。
首先,我们显然必须在Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU打开密钥。我们需要存储的数据值在其中一个值上,其中一个值有一个数字作为名称。MRUListEx值的第一个字节位于最后一个路径。
HKEY hKey = NULL;
std::wstring regMRUPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regMRUPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
// Handle error
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
// Handle error
std::wstring keyName = std::to_wstring(data[0]);现在我们有了值,我们就可以得到它的数据了。
DWORD reqBuffSize = 0;
statusCode = RegQueryValueExW(hKey, keyName.data(), NULL, NULL, NULL, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
BYTE pathData[reqBuffSize];
statusCode = RegGetValueW(hKey, L"", keyName.data(), RRF_RT_REG_BINARY, NULL, &pathData, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.我们还有另一个问题:这条路是无法直接进入的。可执行文件的名称是以实际路径为前缀的,所以我们必须删除它。实际上,这很容易。可执行文件名和实际路径之间的分隔符是\0\0\0。
const size_t offset = (lstrlenW(reinterpret_cast<LPCWSTR>(pathData)) * sizeof(wchar_t))
+ (2 * sizeof(wchar_t));所获得的值不是人类可读的,但它实际上存储了一个PIDL,因此我们可以使用SHGetPathFromIDList获得它。微软的网站上并没有很好地记录ITEMIDList,但这里有一种方法可以创建以下内容之一:
IMalloc* pMalloc;
if (FAILED(SHGetMalloc(&pMalloc)))
// handle error, failed to get a pointer to the IMalloc interface.
PIDLIST_ABSOLUTE pIdl = (PIDLIST_ABSOLUTE)pMalloc->Alloc((reqBuffSize - offset) + 1);
pIdl->mkid.cb = (sizeof(USHORT) + (reqBuffSize - offset));
memcpy_s(pIdl->mkid.abID, (reqBuffSize - offset) + 1, pathData + offset, (reqBuffSize - offset) + 1);
PIDLIST_ABSOLUTE pNext = ILGetNext(pIdl);
if (!pNext)
// handle error, failed to get a pointer to the next item
pNext->mkid.cb = 0;
initialDir.reserve(MAX_PATH * 2);ITEMIDList结构有两个成员: cb和abID。cb是USHORT +数据大小的大小,abID是数据本身的指针。最后一个ITEMIDList结构必须将cb设置为零才能标记结束。ILGetNext给我们一个指向下一个元素的指针。
// Finally, return the path!
std::wstring retValue;
retValue.reserve(MAX_PATH);
if (FALSE == SHGetPathFromIDList(pIdl, initialDir.data())) {
// oh no! free the allocated memory and throw an exception
}
pMalloc->Free(pIdl);它并不像在开始时看起来那么困难,但是如果您想获得这个值以便与文件选择器一起使用,建议使用COM,因为它更容易,并且不太容易出错。感谢大家对旅游的回应,祝您今天愉快!
如果要从指定的扩展获取路径,请参见OpenSavePidlMRU键。它的工作原理是一样的,但它需要一些小的调整。
https://stackoverflow.com/questions/73205902
复制相似问题