Page 63 - MSDN Magazine, May 2017
P. 63
And the error code can be exposed to clients using a read-only accessor (getter):
LONG ErrorCode() const noexcept {
return m_errorCode; }
Now that you’ve built this exception class, you can proceed to wrap the RegGetValue C API in a higher-level C++ interface that’s easier to use and less bug-prone.
Reading a DWORD Value from the Registry
Let’s start with a simple operation: using the RegGetValue API to read a DWORD value from the registry. The usage pattern in this case is pretty simple. But first, let’s see what kind of interface can be defined in C++ for managing this case.
Here’s the RegGetValue API prototype:
LONG WINAPI RegGetValue(
Then you can invoke the RegGetValue API:
LONG retCode = ::RegGetValue( hKey,
subKey.c_str(), value.c_str(), RRF_RT_REG_DWORD, nullptr,
&data,
&dataSize );
The input wstring objects are converted to raw C-style string pointers using the wstring::c_str method. The RRF_RT_REG_ DWORD flag restricts the type of the registry value to DWORD. If the registry value you’re attempting to read is of a different type, the RegGetValue function call safely fails.
The last two parameters represent the address of the output buffer (in this case, the address of the data variable) and the address of a variable that stores the size of the output buffer. In fact, on return, RegGetValue reports the size of the data written to the output buffer. In this case of reading a simple DWORD, the size of data is always 4 bytes, that is, sizeof(DWORD). However, this size parameter is more important for variable-size values such as strings; I’ll discuss that later in this article.
After invoking the RegGetValue function, you can check the return code and throw an exception in case of error:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot read DWORD from registry.", retCode}; }
Note that the error code (retCode) returned by RegGetValue is embedded in the exception object and can be later retrieved by the code that will process the exception.
Or, on success, the DWORD data variable can just be returned to the caller:
return data;
That’s it for the function implementation.
The caller can simply invoke this C++ wrapper function with code like this:
DWORD data = RegGetDword(HKEY_CURRENT_USER, subkey, L"MyDwordValue");
Note how simple this code is when compared to the original Reg- GetValue C API call. You just pass a handle to an open registry key (in this example, the HKEY_CURRENT_USER predefined key), a string containing the sub key, and the value name. On success, the DWORD value is returned to the caller. On the other hand, on error, a custom exception of type RegistryError is thrown. This kind of code is higher level and much simpler than invoking RegGetValue. In fact, the complexity of RegGetValue has been hidden inside this custom RegGetDword C++ wrapper function.
You can use the same pattern to read a QWORD (64-bit data) value from the registry; in this case, you just have to substitute the DWORD type for the registry value with the 64-bit ULONGLONG.
Reading a String Value from the Registry
Reading a DWORD value from the registry is fairly simple: just one call to the RegGetValue Win32 API is sufficient. That’s mainly because a DWORD value is of fixed size—four bytes—the size of a DWORD. On the other hand, reading strings from the registry introduces another layer of complexity because strings are variable-size data. The idea in this case is to call the RegGetValue API twice: In the first call, you request this API to return the desired size for the
May 2017 57
_In_ _In_opt_ _In_opt_ _In_opt_ _Out_opt_ _Out_opt_ _Inout_opt_
);
HKEY hkey, LPCTSTR lpSubKey, LPCTSTR lpValue, DWORD dwFlags, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData
As you can see, this C-interface function takes highly generic data, like a void* output buffer (pvData) and an input/output buffer size parameter (pcbData). Moreover, there are C-style strings (lpSubKey and lpValue) that identify the registry key and the specific value name under that key. You can massage this C function prototype a bit, making it simpler for C++ callers.
First, as you’re going to signal error conditions throwing C++ exceptions, the DWORD value read from the registry can just be returned by the C++ wrapper as a return value. This automatically eliminates the need of the raw void* output buffer parameter (pvData) and the associated size parameter (pcbData).
Moreover, as you’re using C++, it’s better to represent Unicode (UTF-16) strings using the std::wstring class instead of C-style raw pointers. So, you can define this much simpler C++ function to read a DWORD value from the registry:
DWORD RegGetDword(
HKEY hKey,
const std::wstring& subKey, const std::wstring& value
)
As you can see, there are no PVOID and LPDWORD parameters; the input strings are passed via const references to std::wstring objects and the value read from the registry is returned as a DWORD by this C++ function. This is definitely a much simpler and higher-level interface.
Now let’s dive into the implementation. As mentioned, the invoke pattern for RegGetValue in this case is fairly simple. You just have to declare a DWORD variable that will store the value read from the registry:
DWORD data{};
Then you need another DWORD variable that represents the size (in bytes) of the output buffer written by RegGetValue. Note that the output buffer in this simple case is just the previous “data” variable, and its size is constantly the size of a DWORD:
DWORD dataSize = sizeof(data);
However, please note that you can’t mark dataSize as “const” because it’s both an input and an output parameter for RegGetValue. msdnmagazine.com