Read Windows Registry with C++

Quick prototype, but it works

winreg.h:

#include <string>
#include <vector>
#include <map>

enum class hive
{
    classes_root = 0,

    local_machine,

    current_user,

};

std::vector<std::wstring> enum_subkeys(hive h, std::wstring path);

std::vector<std::wstring> get_value_names(hive h, std::wstring path);

std::wstring get_value(hive h, const std::wstring& path, const std::wstring& value_name = L"");

winreg.cpp:

# include "winreg.h"
#include <windows.h>
#include <vector>

using namespace std;

#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383

bool get_key_info(HKEY hKey,
    unsigned long& subkey_count, unsigned long& value_count,
    unsigned long& max_name_length, unsigned long& max_value_length)
{
    TCHAR achKey[MAX_KEY_LENGTH];
    DWORD cbName = MAX_KEY_LENGTH;
    TCHAR achClass[MAX_PATH] = TEXT("");  // buffer for class name 
    DWORD cchClassName = MAX_PATH;
    DWORD cSubKeys = 0;
    DWORD cbMaxSubKey;             // longest subkey size 
    DWORD    cchMaxClass;          // longest class string 
    DWORD    cValues;              // number of values for key
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 

    LSTATUS lResult = ::RegQueryInfoKey(hKey,
        achClass,
        &cchClassName,
        nullptr,
        &cSubKeys,  // number of sub-keys (tree in regedit)
        &cbMaxSubKey,
        &cchMaxClass,
        &cValues,   // number of values (right pane in regedit)
        &cchMaxValue,
        &cbMaxValueData,
        &cbSecurityDescriptor,
        &ftLastWriteTime);

    if (lResult == ERROR_SUCCESS)
    {
        subkey_count = cSubKeys;
        value_count = cValues;
        max_name_length = cchMaxValue;
        max_value_length = cbMaxValueData;
        return true;
    }

    return false;
}

vector<wstring> enum_subkeys(hive h, std::wstring path)
{
    vector<wstring> result;

    HKEY hKey;

    LSTATUS lResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &hKey);

    if (lResult != ERROR_SUCCESS) return result;

    TCHAR achKey[MAX_KEY_LENGTH];

    for(int i = 0; ;i++)
    {
        DWORD cbName = MAX_KEY_LENGTH;

        lResult = ::RegEnumKeyEx(hKey, i, achKey, &cbName, nullptr, nullptr, nullptr, nullptr);
        if (lResult == ERROR_SUCCESS)
        {
            result.push_back(achKey);
        }
        else if (lResult == ERROR_NO_MORE_ITEMS)
        {
            break;
        }
        else
        {
            break;
        }
    }

    ::RegCloseKey(hKey);

    return result;
}

vector<wstring> get_value_names(hive h, wstring path)
{
    vector<wstring> result;
    HKEY hKey;

    LSTATUS lResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &hKey);

    if (lResult == ERROR_SUCCESS)
    {
        unsigned long subkey_count, value_count, max_name_length, max_value_length;
        DWORD type;
        if (get_key_info(hKey, subkey_count, value_count, max_name_length, max_value_length) && value_count)
        {
            max_name_length += 4;
            max_value_length += 4;
            vector<TCHAR> achValue(max_name_length);
            vector<byte> achData(max_value_length);

            for (int i = 0; i < value_count; i++)
            {
                unsigned long name_length = max_name_length;
                lResult = ::RegEnumValue(hKey, i,
                    &achValue[0], &name_length,
                    nullptr,
                    &type,  // todo: include type in result
                    nullptr, nullptr);
                if (lResult == ERROR_SUCCESS)
                {
                    wstring name(&achValue[0]);
                    result.push_back(name);
                }

            }
        }
    }

    ::RegCloseKey(hKey);

    return result;
}

std::wstring get_value(hive h, const std::wstring& path, const std::wstring& value_name)
{
    HKEY hKey;

    LSTATUS lResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &hKey);

    if (lResult == ERROR_SUCCESS)
    {
        DWORD dwType;
        DWORD cbData;
        lResult = ::RegGetValue(hKey,
            nullptr,
            value_name.c_str(),
            RRF_RT_ANY,
            &dwType,
            nullptr,
            &cbData);

        if (lResult == ERROR_SUCCESS)
        {
            // dwType contains data type like REG_SZ
            // cbData is buffer length to allocate

            if (dwType == REG_SZ || dwType == REG_EXPAND_SZ)
            {
                vector<byte> buffer(cbData + 1);
                cbData = buffer.size();
                lResult = ::RegGetValue(hKey, nullptr, value_name.c_str(), RRF_RT_ANY, &dwType,
                    &buffer[0],
                    &cbData);

                wstring sv(reinterpret_cast<wchar_t*>(&buffer[0]));
                return sv;
            }
        }

    }

    ::RegCloseKey(hKey);


    return std::wstring();
}

Sample Usage

#include "winreg.h"
#include <string>
#include <iostream>

using namespace std;

wstring abs_root = L"SOFTWARE\\Clients\\StartMenuInternet";

int main()
{
    auto subs = enum_subkeys(hive::local_machine, abs_root);

    for (wstring sub : subs)
    {
        wcout << sub << endl;

        wstring root = abs_root + L"\\" + sub;
        wstring display_name = get_value(hive::local_machine, root);
        wstring open_command = get_value(hive::local_machine, root + L"\\shell\\open\\command");
        wstring http_url_assoc = get_value(hive::local_machine, root + L"\\Capabilities\\URLAssociations", L"http");

    }

    return 0;
}

Have a question⁉ Contact me.