Every once in a while, you discover a new Win32 API that you've never heard of. The other day, one of the guys in my group sent an email extolling the values of a new WIn32 API that was added for Windows Professional X64 edition and Windows Server 2003 SP1 (and of course Windows Vista).
To read a value from the registry, historically you called the RegQueryValueEx. Unfortunately, the RegQueryValueEx API suffered from a number of fatal problems. The biggest one was that it didn't adequately type check the data being returned - for example, if the registry contained a string value, it was possible that the data in the registry might not be null terminated, resulting in the following warning in the documentation:
If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been stored with the proper null-terminating characters. For example, if the string data is 12 characters and the buffer is larger than that, the function will add the null character and the size of the data returned is
13*sizeof(TCHAR)bytes. However, if the buffer is
12*sizeof(TCHAR)bytes, the data is stored successfully but does not include a terminating null. Therefore, even if the function returns ERROR_SUCCESS, the application should ensure that the string is properly terminated before using it; otherwise, it may overwrite a buffer. (Note that REG_MULTI_SZ strings should have two null-terminating characters, but the function only attempts to add one.)
Unfortunately, many people didn't implement this logic correctly (it's quite hard to get this right for all cases). In addition to the null termination issue, the caller needed to deal with ANY data type being returned - you had to add in checks to ensure that the type of data returned matched the type of data you expected. The root cause of this is a "leaky abstraction" issue - the NT base registry API simply stores blobs of data with the type information maintained as metadata alongside the data being stored. Thus when you retrieve a value from the registry, you get the data in the underlying store and the metadata back. But there's no attempt at ensuring that the metadata matches the intent of the application because the intent of the application isn't known.
So a new API was added to the Windows API set that resolves these issues, RegGetValue. I just converted a 50 line routine to use it, the entire routine 50 line routine turned into a one line call to RegGetValue. Using RegGetValue, I was able to remove:
- The code that checked the type of data in the registry
- The logic to handle REG_EXPAND_SZ (it's automatically handled by RegGetValue)
- Code to ensure null termination of the registry string.
- Code to validate that the length of the registry string was "appropriate" (a multiple of 2).
The bottom line was that I was able to remove a whole chunk of potentially buggy code and replace it with a single API call. Heck, I didn't even need to open the registry key, since the RegGetValue API will even open and close the key for you (it opens the key for KEY_QUERY_VALUE if you care).