Home > Enterprise >  Getting the value of OpenSavePidlMRU in the registry
Getting the value of OpenSavePidlMRU in the registry

Time:08-03

I'm trying to get the last opened directory by an open file dialog, and it seems that we can get it by first retrieving the key's name that contains the path using the first BYTE of the MRUListEx key, then the path can be obtained by reading the value of this key's name. MRUListEx key can be find at: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU. The problem is that I don't know how to properly fill the "SHITEMID" structure. (it doesn't let me access the index, and it throws a Memory access violation). I don't even know if the code below is valid in any points.

Sorry, the code is very dirty for the moment but I'll revamps it when I finally find what causes these errors.

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.
    }
}

CodePudding user response:

You don't seem to understand how types and simple arrays work! BYTE* pathData[512]; is not a 512 byte buffer. Use BYTE pathData[512];.

After reading into pathData, call SHGetPathFromIDList((PCIDLIST_ABSOLUTE) pathData, ...);.

That being said, that ComDlg32 key is undocumented and I don't think it stores a pidl so even if your code is corrected it is not going to work.

EnumMRUListW is a documented function you can call but it is not going to help you decode the data.

It looks to me like the location might be prefixed with the name of the .exe so as a minimum you would have to skip (lstrlenW(pathData) 1)*2 bytes before you find the real data...

CodePudding user response:

If you're going for undocumented stuff, here is a code that can dump an MRU list like the LastVisitedPidlMRU one:

// 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: I'm using ATL's smart classes

  • Related