Page 62 - MSDN Magazine, May 2017
P. 62
C++
Use Modern C++ to Access
the Windows Registry
Giovanni Dicanio
The Windows OS exposes a series of C-interface APIs to give developers access to the registry. Some of these APIs are fairly low level and require programmers to pay attention to many details. Starting with Windows Vista, a kind of higher-level API was added to the mix: the RegGetValue function (bit.ly/2jXtfpJ). Before this API was introduced, to read a value from the registry, you first had to open the desired registry key containing the value calling RegOpenKeyEx. Then, you had to call the RegQueryValueEx API, dealing with many complex details. For example, if you read a string value with RegQueryValueEx, the returned string is not guaranteed to be properly NUL-terminated, which can cause a series of dangerous security bugs in your code. To prevent that from happening, you have to pay attention to check if there’s a NUL terminator in the returned string; if there isn’t any, you have to add it. Moreover, you have to make sure you properly close the open key, calling RegCloseKey.
Of course, opening the registry key could fail, so you have to add code to handle that, as well. The RegGetValue API simplifies this workflow, as it automatically opens the desired registry key, closes it after use, and it properly NUL-terminates strings before returning
them to the caller. Despite this simplification, the RegGetValue function is still a low-level C-interface function; moreover, the fact that it can handle several different types of registry values (from DWORDs to strings to binary data), makes its interface compli- cated for you to program.
Thankfully, you can use modern C++ to properly build higher-level abstractions around this RegGetValue Win32 API, offering a sim- plified interface to read values of different types from the registry.
Representing Errors Using Exceptions
The RegGetValue API is a C-interface API and as such it signals error conditions to the caller using return codes. In particular, this function returns a value of type LONG: ERROR_SUCCESS (that is, zero) in the case of success and a different value in the case of errors. For example, if the output buffer provided by the caller isn’t large enough for the API to write its data, the function returns ERROR_MORE_DATA. For building a higher-level C++ interface around this C API, you can define a C++ exception class to represent errors. This class can be derived from the standard std::runtime_error class and you can embed the LONG error code returned by RegGetValue inside it:
class RegistryError
: public std::runtime_error
{ public:
... private:
LONG m_errorCode; };
In addition, other information pieces can be embedded in the exception object; for example, the HKEY and the name of the sub key. This is just a basic implementation.
You can add a constructor to create an instance of this exception class using an error message and the return code from the failed RegGetValue call:
RegistryError(const char* message, LONG errorCode) : std::runtime_error{message}
, m_errorCode{errorCode}
{}
This article discusses:
• Wrapping some Win32 registry C API functions in modern C++ code
• Wrapping Win32 return codes in C++ exception classes
• Interfacing the STL std::wstring class with Win32 C APIs that
operate with raw C character pointers and raw C buffers
• Wrapping the Win32 C RegGetValue API in higher-level C++ code to read values from the registry
Technologies discussed:
C++, Win32, STL, Registry, Exceptions
Code download available at:
msdn.com/magazine/0517magcode
56 msdn magazine