Search code examples
visual-c++mfcregistry

Enumerating subkeys with CSettingsStore


I have been looking at using CSettingsStore class.

I know how to read a value from the registry. Example:

CSettingsStore store(TRUE, TRUE);

if (store.Open(_T("Software\\TruckleSoft\\VisitsRota")))
{
    if (store.Read(_T("AppPath"), m_strPathVisitsRota))
    {
        //yes, but is the path still valid
        if (!PathFileExists(m_strPathVisitsRota))
        {
            // it exists
            m_strPathVisitsRota = _T("");
        }
    }
}

Now, in the documentation is states:

The security access depends on the bReadOnly parameter. If bReadonly is FALSE, the security access will be set to KEY_ALL_ACCESS. If bReadyOnly is TRUE, the security access will be set to a combination of KEY_QUERY_VALUE, KEY_NOTIFY and KEY_ENUMERATE_SUB_KEYS.

So it implies you can enumerate sub keys. But I can't find an example explaining about to enumerate a set of key / value pairs using this class.


Solution

  • Note that KEY_ENUMERATE_SUB_KEYS is a flag which request access to enumerate the subkeys, it doesn't actually do it.

    KEY_ALL_ACCESS is a combination of different flags, including KEY_ENUMERATE_SUB_KEYS. So in both cases enumeration access is requested.

    To enumerate values we need ::RegEnumValue API, this method is not wrapped in CRegKey, but it doesn't matter, we can get HKEY directly.

    Here is example, based on your code and Microsoft sample

    void CMySettingsStore::EnumKeys(std::vector<CString>& vec)
    {
        vec.clear();
        DWORD subkey_total;
        if (ERROR_SUCCESS != RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL, 
            &subkey_total, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
            return;
        wchar_t buf[1024];
        for (DWORD i = 0; i < subkey_total; i++)
        {
            DWORD len = _countof(buf);
            if (ERROR_SUCCESS == m_reg.EnumKey(i, buf, &len))
                vec.push_back(buf);
        }
    }
    
    void CMySettingsStore::EnumValues(std::vector<CString>& vec)
    {
        vec.clear();
        DWORD values_total;
        if (ERROR_SUCCESS != RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL, 
            NULL, NULL, NULL, &values_total, NULL, NULL, NULL, NULL))
            return;
        wchar_t buf[1024];
        for (DWORD i = 0; i < values_total; i++)
        {
            DWORD len = _countof(buf);
            if (ERROR_SUCCESS == RegEnumValue(m_reg.m_hKey, i, buf, &len,
                NULL, NULL, NULL, NULL)) 
                vec.push_back(buf);
        }
    }
    

    Udpate from Question Author

    This code does not resolve all the code analysis warnings but it does add error handling and support for obtaining the value types:

    #include "stdafx.h"
    #include "MySettingsStore.h"
    
    CMySettingsStore::CMySettingsStore(BOOL bAdmin, BOOL bReadOnly)
        : CSettingsStore(bAdmin, bReadOnly)
    {
    
    }
    
    bool CMySettingsStore::EnumKeys(std::vector<CString>& vec)
    {
        vec.clear();
    
        DWORD subkey_total{}, lResult{};
        if (RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL,
            &subkey_total, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
        {
            const DWORD dwError = ::GetLastError();
            AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
            return false;
        }
    
        wchar_t buf[1024];
        for (DWORD i = 0; i < subkey_total; i++)
        {
            DWORD len = _countof(buf);
            lResult = m_reg.EnumKey(i, buf, &len);
            if(lResult == ERROR_SUCCESS || ERROR_NO_MORE_ITEMS)
                vec.push_back(buf);
        }
    
        if (lResult != ERROR_NO_MORE_ITEMS)
        {
            const DWORD dwError = ::GetLastError();
            AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
            return false;
        }
        
        return true;
    }
    
    bool CMySettingsStore::EnumValues(std::map<CString, DWORD>& map)
    {
        map.clear();
        DWORD values_total{}, dwType{}, lResult{};
        if (RegQueryInfoKey(m_reg.m_hKey, NULL, NULL, NULL,
            NULL, NULL, NULL, &values_total, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
        {
            const DWORD dwError = ::GetLastError();
            AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
            return false;
        }
    
        wchar_t buf[1024];
        for (DWORD i = 0; i < values_total; i++)
        {
            DWORD len = _countof(buf);
    
            lResult = RegEnumValue(m_reg.m_hKey, i, buf, &len,
                NULL, &dwType, NULL, NULL);
            if(lResult == ERROR_SUCCESS || lResult == ERROR_NO_MORE_ITEMS)
                map.emplace(buf, dwType);
        }
    
        if (lResult != ERROR_NO_MORE_ITEMS)
        {
            const DWORD dwError = ::GetLastError();
            AfxMessageBox(GetLastErrorAsString(dwError), MB_OK | MB_ICONWARNING);
            return false;
        }
    
        return true;
    }
    
    CString CMySettingsStore::GetLastErrorAsString(DWORD dwError)
    {
        LPVOID  lpMsgBuf{};
        CString strError;
    
        ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
            (LPTSTR)&lpMsgBuf,
            0,
            nullptr);
    
        strError = static_cast<LPTSTR>(lpMsgBuf);
    
        LocalFree(lpMsgBuf);
    
        return strError;
    }