How long can a registry key name really be?
During the development of Profile Migrator we recently received a bug report that made no sense at first sight. Namely the Windows API funtion RegEnumKeyEx returned ERROR_MORE_DATA indicating that the buffer size for a registry key name was to small. The odd part was that the buffer was always set to the maximum length of a registry key name. Let me explain in more detail:
Profile Migrator as the name says is the new tool to keep your users working without interruption while the IT infrastructure changes by offering a simple solution to keeping a user's configuration of Windows and applications when you're upgrading an OS. Migrating a user profile requires accessing the file system and registry by using the provided functions of Windows API.
The appropriate API function for listing all subkeys in a specific registry key is RegEnumKeyEx. A typical call to this method looks like this:
DWORD nError = RegEnumKeyEx(HKEY_CURRENT_USER, nIndex, sKeyFound, &nBuffer, NULL, NULL, NULL, NULL);
RegEnumKeyEx takes the registry key that is being enumerated, an index of the current subkey, a buffer for the received subkey name, a buffer length and some more parameters that don't matter for this case. The two important parameters are:
- LPTSTR lpNameL - This parameter takes the string buffer that will be filled with the enumerated key name.
- LPDWORD lpcName - The length of the provided buffer must be specified in this parameter. After calling the function it will contain the actual length of the registry key name.
So how much buffer do I need? MSDN tells us that we can either use RegQueryInfoKeyEx to get the maximum length of a registry key name or we use the registry size limit for registry key names, which is 255 characters. Add one for the terminating null character and you should be good to go. Since RegEnumKeyEx returns the used buffer size along with the subkey name and the maximum buffer size of 256 is not a big waste of memory, we always initialized the buffer with a size of 256 characters. This way we'd never run into receiving ERROR_MORE_DATA while enumerating subkeys.
Now the bug report I mentioned at the beginning claimed just that. RegEnumKeyEx had failed with ERROR_MORE_DATA. How could this have happened? The buffer is always set to 256 characters. Registry key names cannot be longer than 255 characters. What was going on?
The first naive approach was to check the limits of registry key names by using regedit.exe. As expected, you'll receive an error message, if you typed more than 255 characters. The second step was to load the ntuser.dat in question and browse the registry key that caused the error. It contained quite a few subkeys, but none of them was longer than allowed. So how could this happen?
We used the debugger and confirmed the maximum buffer size and all of the processing in the method doing the enumeration, but could not figure out, why this was happening. Finally we added a call to RegQueryInfoKeyEx, which returns the maximum registry key name length without the terminating null character. Now according to the documentation this could never be larger than 255. Want to take a guess? Yes, it returned 256.
So if a registry key with a longer name than this can exist, how do you create one? Turns out that this is really simple: Use RegCreateKeyEx and tell it to add a subkey with a name that contains 256 letters and the null at the end, making it 257 characters long. Instead of returning in an error, the key is created. RegCreateKeyEx doesn't seem to care, if the last character is null or not. It does however check, if the limit of 256 is exceeded and ignores the following null there. Trying to create a subkey with 257 or more letters will return ERROR_INVALID_PARAMETER.
I've created a sample Visual Studio 2010 project (download below this article) that demonstrates the issue. If you run it, it will stop after having created a 256 letter volatile key in HKEY_CURRENT_USER and calling RegEnumKeyEx with a buffer of 256. You can check for yourself, if you find the key using regedit.exe or other programs. If you're done, hit any key and the key will be deleted again. (Yes, RegDeleteKeyEx acts like RegCreateKeyEx and accepts 256 letters and a null.)
What about .NET, you might ask. The framework is very good at encapsulating API functions, but it also limits the functionality. Try using Registry.GetSubKeyNames() and an exception will be thrown telling you that there is more data. That is the right error message, but there is nothing you can do to increase the buffer and avoid it.
Admittedly this issue may not occur very often, if you are not scanning a user's registry entirely, but it should be noticed that the issue occured in a registry key of a very unpopular and rarely used application:
To avoid running into this issue, you can either use RegQueryInfoKeyEx and use that result or you set the maximum buffer to 257. Both solutions work.