/ReadStringsFromRegistry

C++/Win32 code to read strings from the Windows registry

Primary LanguageC++MIT LicenseMIT

Reading String Values From the Windows Registry

By Giovanni Dicanio

To retrieve a string value from the Windows registry, the Win32 RegGetValue() API can be used.

The prototype of this function is showed here:

LONG WINAPI RegGetValue(
  _In_         HKEY hkey,
  _In_opt_     LPCTSTR lpSubKey,
  _In_opt_     LPCTSTR lpValue,
  _In_opt_     DWORD dwFlags,
  _Out_opt_    LPDWORD pdwType,
  _Out_opt_    PVOID pvData,
  _Inout_opt_  LPDWORD pcbData
);

Since in general the size of the string to be retrieved is not known a priori, we could call the RegGetValue() function twice.

The purpose of the first call is to get a size value to allocate a buffer large enough to store the string. To get this buffer size, the RegGetValue() function is called passing nullptr as argument for the pvData parameter. The pcbData parameter points to a DWORD that will store the size of the data (i.e. the size for the string buffer), in bytes.

    // First call: get a large enough size to allocate destination string buffer
    DWORD keyType = 0;
    DWORD dataSize = 0;
    const DWORD flags = RRF_RT_REG_SZ; // Only read strings (REG_SZ)
    LONG result = ::RegGetValue(
            key, 
            subKey,
            valueName, 
            flags, 
            &keyType, 
            nullptr, // pvData == nullptr ? Request buffer size
            &dataSize);

Once we have this size value, a CString instance can be allocated with a proper buffer size. For that purpose, the CString::GetBuffer() method can be used:

    // Create a CString with large enough internal buffer
    CString text;
    const DWORD bufferLength = dataSize / sizeof(WCHAR); // length in WCHAR's
    WCHAR* const textBuffer = text.GetBuffer(bufferLength);

Then, the RegGetValue() function is called again, this time passing a valid buffer pointer for the pvData parameter, and a valid maximum buffer size. Upon successful completion of this second call, the RegGetValue() function will store the actual string value into the provided buffer:

    // Second call: read string value from the Registry into local string buffer
    result = ::RegGetValue(
            key, 
            subKey,
            valueName, 
            flags, 
            nullptr, 
            textBuffer, // Write string in this destination buffer
            &dataSize);

Note that the RegGetValue() function may overestimate the size of the string buffer in the first call, to make extra room for a terminating NUL character (in case that would be absent in the string value stored in the registry). Instead, the size value returned by the second RegGetValue() call reflects the actual size of the string written to the destination buffer (including the terminating NUL). Since the returned string is NUL-terminated, a simple call to CString::ReleaseBuffer() would be fine, since this method scans the string buffer, and stops when it finds the NUL-terminator. However, we can slightly optimize our code here (and avoid a “premature pessimization”), calling CString::ReleaseBufferSetLength(), since we already know the actual string length (in fact, this piece of information was passed by the second call of RegGetValue()):

    const DWORD actualStringLength = dataSize / sizeof(WCHAR);
 
    // -1 to exclude the terminating NUL
    text.ReleaseBufferSetLength(actualStringLength - 1);

Now the CString text instance contains the string value read from the Windows registry, ready for further processing.

Note that the aforementioned calls to the RegGetValue() function may fail. To handle those failures, C++ exceptions can be thrown. There are several options to do that in code. For example, it’s possible to derive some C++ exception class from std::exception (the STL base class for exceptions); or, in a more ATL-like style, it’s possible to throw instances of CAtlException with an embedded HRESULT, carrying information about the particular error occurred, e.g.:

    if (result != ERROR_SUCCESS)
    {
        // Error
        AtlThrow(HRESULT_FROM_WIN32(result));
    }

P.S.

While the STL’s std::wstring class could be also used to store string values read from the Windows registry, in general the CString class is better suited for interacting with the Win32 API layer of C++ applications. For example: CString is used at MFC class methods interfaces, as well as in ATL and WTL interfaces.