Page 64 - MSDN Magazine, May 2017
P. 64

output string buffer. Next, you dynamically allocate a proper size buffer. Finally, you make a second call to RegGetValue to actually write the string data into the previously allocated buffer. (This pattern was discussed in detail in my previous article, “Using STL Strings at Win32 API Boundaries,” at msdn.com/magazine/mt238407).
First, take a look at the prototype of the C++ higher-level wrapper function:
std::wstring RegGetString( HKEY hKey,
const std::wstring& subKey, const std::wstring& value
)
As in the DWORD case, this is much simplified with respect to the original complex RegGetValue C API prototype. The string value is returned from the function as a std::wstring instance. Instead, in case of errors, an exception is thrown. The sub-key name and the value name are passed as input wstring const reference parameters, as well.
Now I’ll discuss the implementation code.
As I wrote, the idea is to first call the RegGetValue API to get the size of the output buffer to store the string value:
DWORD dataSize{};
LONG retCode = ::RegGetValue(
hKey, subKey.c_str(), value.c_str(), RRF_RT_REG_SZ, nullptr, nullptr, &dataSize
);
You can see a call syntax similar to the previous DWORD value case. The wstring objects are converted to C-style string pointers invoking the wstring::c_str method. The RRF_RT_REG_SZ flag in this case restricts the valid registry type to the string type (REG_SZ). On success, the RegGetValue API will write the desired output buffer size (expressed in bytes) in the dataSize variable.
On failure, you have to throw an exception of the custom RegistryError class:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot read string from registry", retCode}; }
Now that you know the desired output buffer size, you can allocate a wstring object of the required size for the output string:
std::wstring data;
data.resize(dataSize / sizeof(wchar_t));
Note that the dataSize value returned by RegGetValue is expressed in bytes, but the wstring::resize method expects a size expressed in wchar_t count. So, you have to scale from bytes to wchar_t, dividing the former byte size value by sizeof(wchar_t).
Now that you have a string with enough room allocated, you can pass a pointer to its internal buffer to the RegGetValue API, which this time will write the actual string’s data into the provided buffer:
retCode = ::RegGetValue( hKey,
subKey.c_str(), value.c_str(), RRF_RT_REG_SZ, nullptr, &data[0], &dataSize
);
The &data[0] is the address of the wstring internal buffer that will be written by the RegGetValue API.
As usual, it’s important to verify the result of the API call and throw an exception in case of error:
if (retCode != ERROR_SUCCESS) {
throw RegistryError{"Cannot read string from registry", retCode};
}
Note that on success, RegGetValue writes the actual result string size (in bytes) in the dataSize variable. You must resize the wstring object according to this size. As dataSize is expressed in bytes, it’s better to con- vert it to the corresponding wchar_t count when dealing with wstrings:
DWORD stringLengthInWchars = dataSize / sizeof(wchar_t);
Moreover, dataSize includes the terminating NUL character for the output string. However, wstring objects are already NUL- terminated, so you must pay attention to avoid a spurious and bogus double-NUL termination for the read string. You have to chop off the NUL-terminator written by RegGetValue:
stringLengthInWchars--; // Exclude the NUL written by the Win32 API data.resize(stringLengthInWchars);
Note that the RegGetValue API guarantees a NUL-terminated string on success, even if the original string stored in the registry wasn’t NUL-terminated. This is a much safer behavior than the older RegQueryValueEx API, which didn’t guarantee NUL-termination for the returned strings. So, the caller had to write additional code to properly take that case into account, increasing the overall code complexity and bug surface area.
Now that the data variable contains the string value read from the registry, you can return it to the caller on function exit:
return data;
Once you have this convenient RegGetString C++ wrapper around the RegGetValue low-level C API, you can invoke it like this:
wstring s = RegGetString(HKEY_CURRENT_USER, subkey, L"MyStringValue");
As in the DWORD case, you’ve raised the level of abstraction from the RegGetValue Win32 API, providing an easy-to-use and hard-to- misuse C++ wrapper function to read a string value from the registry. All the details and complexity of dealing with the RegGetValue API are safely hidden inside the body of this custom RegGetString C++ function.
Reading Multi-String Values from the Registry
Another type of registry value is the so-called “multi-string”: Basically, this is a set of double-NUL-terminated strings packed in a single registry value. This double-NUL-terminated string data structure consists of a series of C-style NUL-terminated strings that occupy adjacent memory locations. The end of the sequence is marked by an additional NUL ter- minator, so the whole structure is terminated by two NULs. For more details on this data structure, see the blog post, “What Is the Format of a Double-Null-Terminated String with No Strings?” (bit.ly/2jCqg2u).
The usage pattern of the RegGetValue Win32 API in this case is very similar to the previous case of single strings. That is, first the Reg- GetValue API is invoked to get the size of the whole destination buffer containing the desired data (in this case, the whole sequence of adjacent strings terminated with a double-NUL). Then, a buffer of such size is dynamically allocated. And, finally, the RegGetValue function is called for the second time, passing the address of the previously allocated buf- fer so the API can write the actual multi-string data into that buffer.
In this case, you have to pay attention to the data structure stor- ing the double-NUL-terminated string. In fact, while a std::wstring can properly contain embedded NULs, it could be potentially used
58 msdn magazine
C++


































































































   62   63   64   65   66