Page 66 - MSDN Magazine, May 2017
P. 66
to store a double-NUL-terminated string structure, but I prefer to raise the level of abstraction and parse the double-NUL-terminated string into a higher-level, more convenient vector<wstring>.
So, the prototype of your C++ wrapper function to read multi- string values from the registry can look like this:
std::vector<std::wstring> RegGetMultiString( HKEY hKey,
const std::wstring& subKey,
const std::wstring& value
)
On success, the multi-string will be returned to the caller as a nice vector<wstring>. On the other hand, on error, an exception in the usual RegistryError form will be thrown.
Inside the body of your C++ wrapper function, first you invoke the RegGetValue API to get the size of the desired output buffer to store the multi-string:
DWORD dataSize{};
LONG retCode = ::RegGetValue(
hKey, subKey.c_str(), value.c_str(), RRF_RT_REG_MULTI_SZ, nullptr,
nullptr,
&dataSize
);
Note the use of the RRF_RT_REG_MULTI_SZ flag this time to specify the multi-string registry value type.
As usual, in case of error, an exception is thrown, embedding the error code in the RegistryError object:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot read multi-string from registry", retCode}; }
On success, you allocate a buffer of proper size, to store the whole multi-string:
std::vector<wchar_t> data; data.resize(dataSize / sizeof(wchar_t));
I consider a vector<wchar_t> much clearer than a wstring to repre- sent the multi-string raw buffer. Note also that the size value returned by the RegGetValue API is expressed in bytes, so it must be properly con- verted to a wchar_t count before passing it to the vector::resize method.
Then, the RegGetValue API can be invoked for the second time, to write the actual multi-string data into the buffer previously allocated:
At this point, the data variable (which is a vector<wchar_t>) stores the double-NUL-terminated string sequence. The last step is to parse this data structure and convert it into a higher-level, more convenient vector<wstring>:
// Parse the double-NUL-terminated string into a vector<wstring> std::vector<std::wstring> result;
const wchar_t* currStringPtr = &data[0];
while (*currStringPtr != L'\0')
{
// Current string is NUL-terminated, so get its length with wcslen const size_t currStringLength = wcslen(currStringPtr);
// Add current string to result vector result.push_back(std::wstring{ currStringPtr, currStringLength }); // Move to the next string
currStringPtr += currStringLength + 1;
}
Finally, the result vector<wstring> object can be returned to the caller:
return result;
This RegGetMultiString C++ wrapper can simply be invoked, like this:
vector<wstring> multiString = RegGetMultiString( HKEY_CURRENT_USER,
subkey,
L"MyMultiSz"
);
Again, all the complexity of the Win32 RegGetValue API has been hidden behind a high-level convenient C++ interface.
Enumerating Values Under a Registry Key
Another common Windows registry operation is the enumeration of the values under a given registry key. Windows provides the RegEnumValue API (bit.ly/2jB4kaV) for this purpose. Here, I’ll show how to use this API to get a list of the names and types of the values located under a given registry key, wrapping the enumeration process in a convenient higher-level C++ function. Your custom C++ function can take as input a valid HKEY handle associated to the key you want to enumerate. On success, this custom C++ wrapper function will return a vector of pairs: The first item in the pair will be a wstring containing the value name and the second item a DWORD representing the value type. So, the prototype of this C++ wrapper function will look like this:
std::vector<std::pair<std::wstring, DWORD>> RegEnumValues(HKEY hKey)
Now I’ll discuss the details of the enumeration process. The idea is to first call the RegQueryInfoKey (bit.ly/2jraw2H) API to get some
retCode = ::RegGetValue( hKey,
subKey.c_str(), value.c_str(), RRF_RT_REG_MULTI_SZ, nullptr,
&data[0],
&dataSize );
The &data[0] argument points to the begin- ning of the output buffer.
Again, you have to check the API return code and signal errors throwing a C++ exception:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot read multi-string" from registry", retCode};
}
It’s also good to properly resize the data buffer with the dataSize value returned as an output parameter by the RegGetValue API:
data.resize( dataSize / sizeof(wchar_t) );
60 msdn magazine
Figure 1 Invoking the RegQueryInfoKey API
useful pre-enumeration information, like the total value count and the maximum length of value names under the given registry key, as shown in Figure 1.
Note that I passed nullptr for the pieces of information in which I’m not interested. Of course, you have to check the return value and throw an exception if something went wrong when calling the aforementioned API:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot query key info from" the registry", retCode};
}
According to the Windows Dev Center RegQueryInfoKey function page (bit.ly/2lctUDt), C++
DWORD valueCount{};
DWORD maxValueNameLen{};
LONG retCode = ::RegQueryInfoKey(
);
hKey,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &valueCount, &maxValueNameLen,
nullptr, nullptr, nullptr
// No // No // No
// No user-defined class
// No user-defined class size // Reserved
// No // No // No
subkey count subkey max length subkey class length
max value length security descriptor last write time