Registry Features User's Guide

This describes the classes and miscellaneous features of the system registry support in Classics.

General

The Win32 API offers a number of functions for manipulating the registry. For an improved object-oriented C++-centric way to access the registry, nothing comes to mind. So this effort concentrates on making the existing primitves properly robust, and uses classes where such use is obvious and not contrived.

Beyond better-wrapped primitives for accessing the registry, the real benefit comes from higher level functions. [more later...]

More Robust Primitives

The Classics registry primitives address issues with the Win32 API concerning error checking and Unicode/ANSI transparancy.

For some strange reason, the RegXxx primitives in the Win32 API return an error code directly as the function result, which is different from normal Win32 API policy which is to return a Boolean success (1) or failure (0) and in the latter case set the error code to be retreived using GetLastError.

The Classics functions uniformly use exceptions to indicate unexpected failures. In the case of the registry functions, the return value is used to return something useful, and errors are indicated by throwing a highly detailed exception object, as usual.

This difference in the error handling of the registry Win32 functions is something that appears to have gone unnoticed by other programmers and Microsoft, too. In addition, it was clearly not tested properly. Specifically, a problem shows up with the Unicode forms of the functions under Win95. If the Unicode form is called, the correct behavior would be to return ERROR_CALL_NOT_IMPLEMENTED (whose value happens to be 120). Instead, the functions return ERROR_SUCCESS, indicating normal completion, but don't actually do anything and return bogus results such as invalid HKEYs.

This problem seems to be caused by the implementation of the function returning FALSE to indicate an error and setting the 120 code into the GetLastError area. Since ERROR_SUCCESS also happens to be zero, you have the appearance of a successful function. Since the registry functions don't normally modify the GetLastError value (that is, they don't clear it out on success), a work around is to explicitly clear it before making the call, and then if a zero is received as a return value, check GetLastError. If set, the zero must have been FALSE, otherwise it can be assumed to be ERROR_SUCCESS.

This problem appears to only effect the Unicode not-implemented stubs, so the extra logic is only needed when deciding whether Unicode forms of the functions are available. As the distinction between Unicode and ANSI is taken care of automatically by Classics, this code is used internally the first time a registry function is called in order to determine the availability of Unicode support, and after that more efficient code is used.

Another discrepency, though less of a problem, exists in the documentation. The Platform SDK documents the legal characters in a registry Key name to consist of "printable ANSI characters" in the range of '\x20' through '\x7e' inclusive, except for space, '*', '?', and backslash. If true, it means that there is no real distinction between ANSI and Unicode for key names, since key names are restricted to using 92 possible characters. However, inspecting the registry it is instantly clear that Microsoft doesn't read their own documentation, as there are lots of keys present such as "Control Panel" and "Internet Explorer", not to mention the key named "*" under HKEY_CLASSES_ROOT.

Experiments indicate that as far as actual implementation goes, anything is accepted including the proscribed characters listed in the documentation. So the Classics functions do not do any additional error checking, and are written to allow any Unicode strings. In addition, the registry_UT.cxx program includes tests to see what is actually allowed (what works) on the current system.

registry_key class

predefined keys

enumerating keys and values

higher level functions