Page 68 - MSDN Magazine, May 2017
P. 68
the size returned for the maximum length of value names (stored in the maxValueNameLen variable in the previous code) does not include the terminating NUL; so, let’s adjust this value, adding one to take the terminating NUL into account when you allocate a buffer for reading value names:
maxValueNameLen++;
Then you can allocate a buffer of proper size to read the value names at each enumeration step; an efficient low-overhead std::unique_ptr<wchar_t[]> can be used for that purpose:
auto nameBuffer = std::make_unique<wchar_t[]>(maxValueNameLen);
The result of the enumeration, in the form of pairs of value name and value type, can be stored in a std::vector:
std::vector<std::pair<std::wstring, DWORD>> values;
You’ll progressively add content to this vector during the enu- meration process and then “values” will be returned to the caller when the enumeration is complete.
Then you can use a for loop, calling the RegEnumValue API repeatedly and enumerating a new value at each iteration step:
for (DWORD index = 0; index < valueCount; index++) {
// Call RegEnumValue to get data of current value ... }
Note that you got valueCount from the initial pre-enumeration RegQueryInfoKey call.
Inside the body of the for loop, the RegEnumValue API can be called to get the desired information for the current value. In this context, you’re interested in the value’s name and value’s type. The value’s name will be read in the nameBuffer previously allocated; the value’s type will be stored in a simple DWORD. So, inside the body of the for loop, you can write code like this:
DWORD valueNameLen = maxValueNameLen; DWORD valueType{};
retCode = ::RegEnumValue(
hKey,
index, nameBuffer.get(), &valueNameLen,
for (const auto& v : values) {
// Process v.first (value's name) and v.second (value's type) // ...
A similar coding pattern can be used to enumerate the sub-keys under a given registry key; in this case, the Win32 RegEnumKeyEx (bit.ly/2k3VEX8) API must be used instead of the previously discussed RegEnumValue. The code of such sub-key enumeration function is provided in the download associated with this article.
A Safe Resource Manager for Raw HKEY Handles
Registry keys represented by the raw HKEY Win32 handle type can be safely and conveniently wrapped in a C++ resource manager class. The class destructor will properly call the RegCloseKey API on the wrapped raw handle to automatically close the handle. Moreover, move semantics operations like a move constructor and a move assignment operator can be defined to efficiently transfer owner- ship of the wrapped handle between different instances of the C++ resource manager class. For efficiency, all the class methods that don’t throw exceptions are marked as noexcept, letting the C++ compiler emit more optimized code. This convenient key resource manager C++ class, named RegKey, is implemented in the Registry.hpp file accompanying this article. In this reusable header-only file, you’ll also find the implementations of a couple of helper functions: RegOpenKey and RegCreateKey, that respectively wrap the Win32 APIs RegOpenKeyEx and RegCreateKeyEx, returning an HKEY handle safely wrapped in the aforementioned C++ resource manager class. In case of errors, those C++ functions throw a RegistryError exception, wrapping the error code returned by the raw C-interface Win32 APIs.
Wrapping Up
The RegGetValue Win32 API provides a relatively higher-level inter- face for reading values from the Windows registry when compared to lower-level APIs such as RegQueryValueEx. RegGetValue also offers a safer interface that, for example, guarantees the returned strings are properly NUL-terminated. Nonetheless, RegGetValue is still a C-interface low-level API that requires the programmer to pay attention to many details, and programming against it can lead to bug-prone complex code. This article showed how a convenient, easy-to-use and hard-to-misuse modern C++ interface can be built to hide the complexities of the RegGetValue API, while simplifying access to the Windows registry. Moreover, the RegEnumValue API was wrapped in a convenient higher-level C++ function to enu- merate all the values under a given registry key. The source code containing the implementation of the functions and classes dis- cussed in this article can be found in a reusable header-only form (in the Registry.hpp file) in the article’s download. n
Giovanni Dicanio is a computer programmer specializing in C++ and the Windows OS, a Pluralsight author (bit.ly/GioDPS) and a Visual C++ MVP. Besides programming and course authoring, he enjoys helping others on forums and communities devoted to C++. Reach him via e-mail at giovanni.dicanio@gmail.com. He also blogs on msmvps.com/gdicanio.
Thanks to the following technical experts for reviewing this article: David Cravey and Marc Gregoire
nullptr, &valueType, nullptr, nullptr
// Reserved
// Not interested in data
// Not interested in data size
As usual, it’s good practice to check the API return value and throw an exception on error:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot get value info from the registry", retCode}; }
On success, the RegEnumValue API will write the value’s name in the provided nameBuffer, and the value’s type in the valueType variable. So, you can build a pair<wstring, DWORD> with these two pieces of information and add this information pair to the enumeration result vector:
values.push_back(std::make_pair(
std::wstring{ nameBuffer.get(), valueNameLen }, valueType
));
After the for loop, the result “values” vector can be returned to the caller:
return values;
The caller can then enumerate all the values under a registry key by just calling the C++ wrapper function like this:
auto values = RegEnumValues(hKey);
// For each value
}
62 msdn magazine
C++