[c++] How to read a value from the Windows registry

Given the key for some registry value (e.g. HKEY_LOCAL_MACHINE\blah\blah\blah\foo) how can I:

  1. Safely determine that such a key exists.
  2. Programmatically (i.e. with code) get its value.

I have absolutely no intention of writing anything back to the registry (for the duration of my career if I can help it). So we can skip the lecture about every molecule in my body exploding at the speed of light if I write to the registry incorrectly.

Prefer answers in C++, but mostly just need to know what the special Windows API incantation to get at the value is.

This question is related to c++ windows winapi registry

The answer is


Since Windows >=Vista/Server 2008, RegGetValue is available, which is a safer function than RegQueryValueEx. No need for RegOpenKeyEx, RegCloseKey or NUL termination checks of string values (REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ).

#include <iostream>
#include <string>
#include <exception>
#include <windows.h>

/*! \brief                          Returns a value from HKLM as string.
    \exception  std::runtime_error  Replace with your error handling.
*/
std::wstring GetStringValueFromHKLM(const std::wstring& regSubKey, const std::wstring& regValue)
{
    size_t bufferSize = 0xFFF; // If too small, will be resized down below.
    std::wstring valueBuf; // Contiguous buffer since C++11.
    valueBuf.resize(bufferSize);
    auto cbData = static_cast<DWORD>(bufferSize * sizeof(wchar_t));
    auto rc = RegGetValueW(
        HKEY_LOCAL_MACHINE,
        regSubKey.c_str(),
        regValue.c_str(),
        RRF_RT_REG_SZ,
        nullptr,
        static_cast<void*>(valueBuf.data()),
        &cbData
    );
    while (rc == ERROR_MORE_DATA)
    {
        // Get a buffer that is big enough.
        cbData /= sizeof(wchar_t);
        if (cbData > static_cast<DWORD>(bufferSize))
        {
            bufferSize = static_cast<size_t>(cbData);
        }
        else
        {
            bufferSize *= 2;
            cbData = static_cast<DWORD>(bufferSize * sizeof(wchar_t));
        }
        valueBuf.resize(bufferSize);
        rc = RegGetValueW(
            HKEY_LOCAL_MACHINE,
            regSubKey.c_str(),
            regValue.c_str(),
            RRF_RT_REG_SZ,
            nullptr,
            static_cast<void*>(valueBuf.data()),
            &cbData
        );
    }
    if (rc == ERROR_SUCCESS)
    {
        cbData /= sizeof(wchar_t);
        valueBuf.resize(static_cast<size_t>(cbData - 1)); // remove end null character
        return valueBuf;
    }
    else
    {
        throw std::runtime_error("Windows system error code: " + std::to_string(rc));
    }
}

int main()
{
    std::wstring regSubKey;
#ifdef _WIN64 // Manually switching between 32bit/64bit for the example. Use dwFlags instead.
    regSubKey = L"SOFTWARE\\WOW6432Node\\Company Name\\Application Name\\";
#else
    regSubKey = L"SOFTWARE\\Company Name\\Application Name\\";
#endif
    std::wstring regValue(L"MyValue");
    std::wstring valueFromRegistry;
    try
    {
        valueFromRegistry = GetStringValueFromHKLM(regSubKey, regValue);
    }
    catch (std::exception& e)
    {
        std::cerr << e.what();
    }
    std::wcout << valueFromRegistry;
}

Its parameter dwFlags supports flags for type restriction, filling the value buffer with zeros on failure (RRF_ZEROONFAILURE) and 32/64bit registry access (RRF_SUBKEY_WOW6464KEY, RRF_SUBKEY_WOW6432KEY) for 64bit programs.


#include <windows.h>
#include <map>
#include <string>
#include <stdio.h>
#include <string.h>
#include <tr1/stdint.h>

using namespace std;

void printerr(DWORD dwerror) {
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dwerror,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &lpMsgBuf,
        0,
        NULL
    );
    // Process any inserts in lpMsgBuf.
    // ...
    // Display the string.
    if (isOut) {
        fprintf(fout, "%s\n", lpMsgBuf);
    } else {
        printf("%s\n", lpMsgBuf);
    }
    // Free the buffer.
    LocalFree(lpMsgBuf);
}



bool regreadSZ(string& hkey, string& subkey, string& value, string& returnvalue, string& regValueType) {
    char s[128000];
    map<string,HKEY> keys;
    keys["HKEY_CLASSES_ROOT"]=HKEY_CLASSES_ROOT;
    keys["HKEY_CURRENT_CONFIG"]=HKEY_CURRENT_CONFIG; //DID NOT SURVIVE?
    keys["HKEY_CURRENT_USER"]=HKEY_CURRENT_USER;
    keys["HKEY_LOCAL_MACHINE"]=HKEY_LOCAL_MACHINE;
    keys["HKEY_USERS"]=HKEY_USERS;
    HKEY mykey;

    map<string,DWORD> valuetypes;
    valuetypes["REG_SZ"]=REG_SZ;
    valuetypes["REG_EXPAND_SZ"]=REG_EXPAND_SZ;
    valuetypes["REG_MULTI_SZ"]=REG_MULTI_SZ; //probably can't use this.

    LONG retval=RegOpenKeyEx(
        keys[hkey],         // handle to open key
        subkey.c_str(),  // subkey name
        0,   // reserved
        KEY_READ, // security access mask
        &mykey    // handle to open key
    );
    if (ERROR_SUCCESS != retval) {printerr(retval); return false;}
    DWORD slen=128000;
    DWORD valuetype = valuetypes[regValueType];
    retval=RegQueryValueEx(
      mykey,            // handle to key
      value.c_str(),  // value name
      NULL,   // reserved
      (LPDWORD) &valuetype,       // type buffer
      (LPBYTE)s,        // data buffer
      (LPDWORD) &slen      // size of data buffer
    );
    switch(retval) {
        case ERROR_SUCCESS:
            //if (isOut) {
            //    fprintf(fout,"RegQueryValueEx():ERROR_SUCCESS:succeeded.\n");
            //} else {
            //    printf("RegQueryValueEx():ERROR_SUCCESS:succeeded.\n");
            //}
            break;
        case ERROR_MORE_DATA:
            //what do I do now?  data buffer is too small.
            if (isOut) {
                fprintf(fout,"RegQueryValueEx():ERROR_MORE_DATA: need bigger buffer.\n");
            } else {
                printf("RegQueryValueEx():ERROR_MORE_DATA: need bigger buffer.\n");
            }
            return false;
        case ERROR_FILE_NOT_FOUND:
            if (isOut) {
                fprintf(fout,"RegQueryValueEx():ERROR_FILE_NOT_FOUND: registry value does not exist.\n");
            } else {
                printf("RegQueryValueEx():ERROR_FILE_NOT_FOUND: registry value does not exist.\n");
            }
            return false;
        default:
            if (isOut) {
                fprintf(fout,"RegQueryValueEx():unknown error type 0x%lx.\n", retval);
            } else {
                printf("RegQueryValueEx():unknown error type 0x%lx.\n", retval);
            }
            return false;

    }
    retval=RegCloseKey(mykey);
    if (ERROR_SUCCESS != retval) {printerr(retval); return false;}

    returnvalue = s;
    return true;
}

The pair RegOpenKey and RegQueryKeyEx will do the trick.

If you use MFC CRegKey class is even more easier solution.


const CString REG_SW_GROUP_I_WANT = _T("SOFTWARE\\My Corporation\\My Package\\Group I want");
const CString REG_KEY_I_WANT= _T("Key Name");

CRegKey regKey;
DWORD   dwValue = 0;

if(ERROR_SUCCESS != regKey.Open(HKEY_LOCAL_MACHINE, REG_SW_GROUP_I_WANT))
{
  m_pobLogger->LogError(_T("CRegKey::Open failed in Method"));
  regKey.Close();
  goto Function_Exit;
}
if( ERROR_SUCCESS != regKey.QueryValue( dwValue, REG_KEY_I_WANT))
{
  m_pobLogger->LogError(_T("CRegKey::QueryValue Failed in Method"));
  regKey.Close();
  goto Function_Exit;
}

// dwValue has the stuff now - use for further processing

Typically the register key and value are constants in the program. If so, here is an example how to read a DWORD registry value Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled:

#include <windows.h>

DWORD val;
DWORD dataSize = sizeof(val);
if (ERROR_SUCCESS == RegGetValueA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\FileSystem", "LongPathsEnabled", RRF_RT_DWORD, nullptr /*type not required*/, &val, &dataSize)) {
  printf("Value is %i\n", val);
  // no CloseKey needed because it is a predefined registry key
}
else {
  printf("Error reading.\n");
}

To adapt for other value types, see https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-reggetvaluea for complete spec.


RegQueryValueEx

This gives the value if it exists, and returns an error code ERROR_FILE_NOT_FOUND if the key doesn't exist.

(I can't tell if my link is working or not, but if you just google for "RegQueryValueEx" the first hit is the msdn documentation.)


This console app will list all the values and their data from a registry key for most of the potential registry values. There's some weird ones not often used. If you need to support all of them, expand from this example while referencing this Registry Value Type documentation.

Let this be the registry key content you can import from a .reg file format:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\added\subkey]
"String_Value"="hello, world!"
"Binary_Value"=hex:01,01,01,01
"Dword value"=dword:00001224
"QWord val"=hex(b):24,22,12,00,00,00,00,00
"multi-line val"=hex(7):4c,00,69,00,6e,00,65,00,20,00,30,00,00,00,4c,00,69,00,\
  6e,00,65,00,20,00,31,00,00,00,4c,00,69,00,6e,00,65,00,20,00,32,00,00,00,00,\
  00
"expanded_val"=hex(2):25,00,55,00,53,00,45,00,52,00,50,00,52,00,4f,00,46,00,49,\
  00,4c,00,45,00,25,00,5c,00,6e,00,65,00,77,00,5f,00,73,00,74,00,75,00,66,00,\
  66,00,00,00

The console app itself:

#include <Windows.h>
#include <iostream>
#include <string>
#include <locale>
#include <vector>
#include <iomanip>

int wmain()
{
    const auto hKey = HKEY_CURRENT_USER;
    constexpr auto lpSubKey = TEXT("added\\subkey");
    auto openedKey = HKEY();
    auto status = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_READ, &openedKey);

    if (status == ERROR_SUCCESS) {
        auto valueCount = static_cast<DWORD>(0);
        auto maxNameLength = static_cast<DWORD>(0);
        auto maxValueLength = static_cast<DWORD>(0);
        status = RegQueryInfoKey(openedKey, NULL, NULL, NULL, NULL, NULL, NULL,
            &valueCount, &maxNameLength, &maxValueLength, NULL, NULL);

        if (status == ERROR_SUCCESS) {
            DWORD type = 0;
            DWORD index = 0;
            std::vector<wchar_t> valueName = std::vector<wchar_t>(maxNameLength + 1);
            std::vector<BYTE> dataBuffer = std::vector<BYTE>(maxValueLength);

            for (DWORD index = 0; index < valueCount; index++) {
                DWORD charCountValueName = static_cast<DWORD>(valueName.size());
                DWORD charBytesData = static_cast<DWORD>(dataBuffer.size());
                status = RegEnumValue(openedKey, index, valueName.data(), &charCountValueName,
                    NULL, &type, dataBuffer.data(), &charBytesData);

                if (type == REG_SZ) {
                    const auto reg_string = reinterpret_cast<wchar_t*>(dataBuffer.data());
                    std::wcout << L"Type: REG_SZ" << std::endl;
                    std::wcout << L"\tName: " << valueName.data() << std::endl;
                    std::wcout << L"\tData : " << reg_string << std::endl;
                }
                else if (type == REG_EXPAND_SZ) {
                    const auto casted = reinterpret_cast<wchar_t*>(dataBuffer.data());
                    TCHAR buffer[32000];
                    ExpandEnvironmentStrings(casted, buffer, 32000);
                    std::wcout << L"Type: REG_EXPAND_SZ" << std::endl;
                    std::wcout << L"\tName: " << valueName.data() << std::endl;
                    std::wcout << L"\tData: " << buffer << std::endl;
                }
                else if (type == REG_MULTI_SZ) {
                    std::vector<std::wstring> lines;
                    const auto str = reinterpret_cast<wchar_t*>(dataBuffer.data());
                    auto line = str;
                    lines.emplace_back(line);
                    for (auto i = 0; i < charBytesData / sizeof(wchar_t) - 1; i++) {
                        const auto c = str[i];
                        if (c == 0) {
                            line = str + i + 1;
                            const auto new_line = reinterpret_cast<wchar_t*>(line);
                            if (wcsnlen_s(new_line, 1024) > 0)
                                lines.emplace_back(new_line);
                        }
                    }
                    std::wcout << L"Type: REG_MULTI_SZ" << std::endl;
                    std::wcout << L"\tName: " << valueName.data() << std::endl;
                    std::wcout << L"\tData: " << std::endl;
                    for (size_t i = 0; i < lines.size(); i++) {
                        std::wcout << L"\t\tLine[" << i + 1 << L"]: " << lines[i] << std::endl;
                    }
                }
                if (type == REG_DWORD) {
                    const auto dword_value = reinterpret_cast<unsigned long*>(dataBuffer.data());
                    std::wcout << L"Type: REG_DWORD" << std::endl;
                    std::wcout << L"\tName: " << valueName.data() << std::endl;
                    std::wcout << L"\tData : " << std::to_wstring(*dword_value) << std::endl;
                }
                else if (type == REG_QWORD) {
                    const auto qword_value = reinterpret_cast<unsigned long long*>(dataBuffer.data());
                    std::wcout << L"Type: REG_DWORD" << std::endl;
                    std::wcout << L"\tName: " << valueName.data() << std::endl;
                    std::wcout << L"\tData : " << std::to_wstring(*qword_value) << std::endl;
                }
                else if (type == REG_BINARY) {
                    std::vector<uint16_t> bins;
                    for (auto i = 0; i < charBytesData; i++) {
                        bins.push_back(static_cast<uint16_t>(dataBuffer[i]));
                    }
                    std::wcout << L"Type: REG_BINARY" << std::endl;
                    std::wcout << L"\tName: " << valueName.data() << std::endl;
                    std::wcout << L"\tData:";
                    for (size_t i = 0; i < bins.size(); i++) {
                        std::wcout << L" " << std::uppercase << std::hex << \
                            std::setw(2) << std::setfill(L'0') << std::to_wstring(bins[i]);
                    }
                    std::wcout << std::endl;
                }
            }
        }
    }

    RegCloseKey(openedKey);
    return 0;
}

Expected console output:

Type: REG_SZ
        Name: String_Value
        Data : hello, world!
Type: REG_BINARY
        Name: Binary_Value
        Data: 01 01 01 01
Type: REG_DWORD
        Name: Dword value
        Data : 4644
Type: REG_DWORD
        Name: QWord val
        Data : 1188388
Type: REG_MULTI_SZ
        Name: multi-line val
        Data:
                Line[1]: Line 0
                Line[2]: Line 1
                Line[3]: Line 2
Type: REG_EXPAND_SZ
        Name: expanded_val
        Data: C:\Users\user name\new_stuff

Examples related to c++

Method Call Chaining; returning a pointer vs a reference? How can I tell if an algorithm is efficient? Difference between opening a file in binary vs text How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Install Qt on Ubuntu #include errors detected in vscode Cannot open include file: 'stdio.h' - Visual Studio Community 2017 - C++ Error How to fix the error "Windows SDK version 8.1" was not found? Visual Studio 2017 errors on standard headers How do I check if a Key is pressed on C++

Examples related to windows

"Permission Denied" trying to run Python on Windows 10 A fatal error occurred while creating a TLS client credential. The internal error state is 10013 How to install OpenJDK 11 on Windows? I can't install pyaudio on Windows? How to solve "error: Microsoft Visual C++ 14.0 is required."? git clone: Authentication failed for <URL> How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" XCOPY: Overwrite all without prompt in BATCH Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory how to open Jupyter notebook in chrome on windows Tensorflow import error: No module named 'tensorflow'

Examples related to winapi

ImportError: no module named win32api Why does CreateProcess give error 193 (%1 is not a valid Win32 app) Dynamically load a function from a DLL How to check if directory exist using C++ and winAPI How to convert char* to wchar_t*? Get current cursor position Check whether a path is valid How do I link to a library with Code::Blocks? Where to find the win32api module for Python? Cannot open include file 'afxres.h' in VC2010 Express

Examples related to registry

IE Enable/Disable Proxy Settings via Registry How to read value of a registry key c# How to add Python to Windows registry How can I enable Assembly binding logging? Assign command output to variable in batch file Registry key for global proxy settings for Internet Explorer 10 on Windows 8 Run reg command in cmd (bat file)? Command line to remove an environment variable from the OS level configuration How to export/import PuTTy sessions list? Check if registry key exists using VBScript