How did registry keys work in 16-bit Windows?


Back in 16-bit Windows, the registry was a very different beast. Originally, the only registry hive was HKEY_CLASSES_ROOT, and the only things it was used for were COM object registration and file type registration. (Prior to the registry, file type registration was done in WIN.INI, and the only verb you could register for was "open".) The registry was stored in a single file, REG.DAT, which could not exceed 64KB in size.

But back to registry keys. In 16-bit Windows, the registry was much simpler. The first program to call RegOpenKey caused the registry to be loaded, and the registry kept a running count of how many registry keys were open. When the matching number of RegCloseKey calls was made, the registry was written back to disk and unloaded from memory. (Yes, this means that if a program leaked a registry key, then the registry never got unloaded. Welcome to 16-bit Windows, which trusted software developers not to be stupid.) The registry key itself was just an index into the raw registry data.

This backgrounder is really just a boring set-up for this little quirk of the RegOpenKey function in Win32:

If this parameter [lpSubKey] is NULL or a pointer to an empty string, the function returns the same handle that was passed in.

This is an example of over-documentation, documenting the implementation rather than the contract. That the same handle was returned in 16-bit Windows is just a side effect of the fact that a registry key was an index. Opening the same key twice leads to the same key, which consequently has the same index (since it's the same thing). Therefore, opening the same key more than once yields the same numeric value for the handle.

On the other hand, in 16-bit Windows, even if you reopened the key and got the same numeric value back, it nevertheless increased the "number of open registry keys" counter, and consequently you had to call RegCloseKey for each successful call to RegOpenKey, even if the numerical handle values were the same (which you shouldn't care about since handles are opaque).

The people designing the 32-bit registry found themselves in a pickle. What should RegOpenKey do if asked to open the same key that was passed in? Should it return a new key which also must be closed with RegCloseKey? Or should it preserve the inadvertently contractual 16-bit behavior and return the same numeric key back? Since kernel handles are not reference-counted, returning the same numeric value back means that when the caller closes the key, they close both that key and the original key passed in (since they are the same thing).

The Win32 folks went for the second option: RegOpenKey on the same key returns the same numeric value back, which must not be closed (for doing so would close the original key as well). Personally, I would have gone for the first option (returning a new handle), but presumably the people who made this decision did so for a good reason. I suspect application compatibility played a major role.

What does this mean for you? It means that you should just plain avoid the RegOpenKey function, since it becomes harder to predict whether you need to close the returned key or not. Sure, if you pass a hard-coded string as the subkey name you can tell, but if the subkey name is dynamically-generated, then there's a possibility that the subkey name is a null string, in which case the returned key shouldn't be closed. Instead, use RegOpenKeyEx, which—since it is a new function in Win32—does not have the compatibility constraints of RegOpenKey. The key returned by RegOpenKeyEx is a brand new key and must be closed. Doesn't matter whether the subkey name is blank or not. Open with RegOpenKeyEx and close with RegCloseKey, end of story.

Comments (22)
  1. J. Edward Sanchez says:

    Related trivia: Back in the Win16 days, the registry was called the "registration database", and regedit.exe was the "Registration Database Editor".

  2. Mike Dimmick says:

    Huh. I always assumed the 32-bit registry predated the 16-bit registry since so much of Windows NT’s configuration uses it, although of course the registry was released originally for Windows 3.1 in 1992 while NT 3.1 was released in 1993.

    I suppose this is probably a case where NT’s implementation came first, in NtCreateKey, then the concept was backported (badly) to 16-bit Windows, leaving a compatibility mess for the Win32 API authors to deal with.

  3. Anonymous says:

    "…trusted software developers not to be stupid."

    Well, there’s your problem.  But seriously, isn’t there only so much you can do to protect against stupidity / maliciousness?  At what point do you have to concede defeat?

  4. Huh Mike? Windows 3.1 was released before Windows NT 3.1, and yet it was "backported (badly)"?

    The registry was implemented as part of OLE 1 (i.e. before COM). This made its first appearance in Windows 3.1, and was then (presumably) ported forward into Win32.

  5. Anonymous says:

    rlipscombe: Mike’s point was probably that the work on NT had been going on for quite some time (even predating the release of Windows 3.0), and, as the Registry now seems so prevalent in every aspect of NT configuration, it would make sense to assume that it was present in some form when the decision was made to include a similar concept in Win 3.1.

  6. CornedBee says:

    Hmmm … trusting a programmer not to be stupid is one thing. Trusting him to never leak a handle is another.

  7. Anonymous says:

    > "…trusted software developers not to be stupid."

    Well, there’s your problem

    Please understand that this was not a problem in the 80’s.  Not everyone was a programmer.  There were not even formalized computer science degrees (in the 70’s up to the 80’s people who entered the programming profession usually had a degree in mathematics).  There were only a handful of programmers, most of whom were smart.

    Jonathan Pryor is right in that Win16 was developed for woefully underpowered computers, as compared to the mainframes that VAX and/or UNIX ran.  But please understand that another factor in deciding to trust developers is that most developers were sane.  Remebmer, it’s the 80’s.  This was before the time of "Learn C++ in 21 Days" and sub-par students that managed to graduate with a BS in computer science and entered the field.

  8. Anonymous says:

    BTW, the new Win32 registry was one of the things backported to Win16 with Windows 95.

  9. Anonymous says:

    "But seriously, isn’t there only so much you can do to protect against stupidity / maliciousness?  At what point do you have to concede defeat?"

    Probably 1995.

  10. Anonymous says:

    "But seriously, isn’t there only so much you can do to protect against stupidity / maliciousness?  At what point do you have to concede defeat?"

    Try 1988 – Morris Worm: unintended side-effects of buffer overflows.

  11. Anonymous says:

    “The Win32 folks went for the second option: RegOpenKey on the same key returns the same numeric value back, which must not be closed (for doing so would close the original key as well). Personally, I would have gone for the first option (returning a new handle), but presumably the people who made this decision did so for a good reason. I suspect application compatibility played a major role.”

    I would have thought that AppCompat would have led to the other decision — after all, it seems less likely that someone was relying on the same numeric value to be returned than the idea that two calls to RegCloseKey would be required.  After all, the ported code would have those two calls, since they’re not allowed to leak handles (as you also pointed out)…

    [That they went for preserving the behavior tells me that there is a strong likelihood that somebody was relying on the numerical value being the same. -Raymond]
  12. Anonymous says:

    > > "…trusted software developers not to be stupid."

    > Well, there’s your problem.

    No, the problem was that Win16 was developed for a resource-contrained (minimal RAM, minimal CPU; 16-bit x86 didn’t support memory protection between processes, etc.), so to get *anything* decent done they had to go with a few "simplifying assumptions."  Sane/decent developers was one of those simplifying assumptions.

    The same assumption is made for e.g. kernel-space device drivers, where if the author screws things up, the system crashes.

    Notice that many of these were cleaned up in Win32, such as the aforementioned RegOpenKeyEx(), *real* process address space separation, etc., with a corresponding increase in machine requirements (32-bit CPU, more RAM, etc.).

  13. Anonymous says:

    I remember the Win16 registry for a different reason. Back then, the computer music format that was the rage was MODs, and the best player was MOD4Win, a nice shareware (now freeware) app for Windows 3.1. Of course, it relied on a key code you got when you registered it, and it tracked the number of days that you used it.

    The thing was, uninstalling and reinstalling never re-enabled the trial functionality, it was always hidden deep within the bowels of Windows. Actually, it was kept in the registry. Since no one knew about it (since a user had no need to ever go in there, unlike today) it was the perfect place to hide stuff. I found out about it when someone hinted that I should look there, and sure enough, deleting those keys reset the timer.

    Fun times.

  14. Anonymous says:

    Nicholas>

    "Please understand that this was not a problem in the 80’s.  Not everyone was a programmer.  There were not even formalized computer science degrees"

    My BSc. degree certificate which was given to me in 1982 quite explicitly states "Computer Science".

    Was I the victim of some elaborate hoax then?

  15. Anonymous says:

    [Pre-empted pre-emptive snarky comment]

    Oh yawn!

    Now, if the article had gone into more detail about how this all worked on Windows 286, that would have been really riveting. And relevant.

    *snicker*

  16. Anonymous says:

    "

    (Yes, this means that if a program leaked a registry key, then the registry never got unloaded.

    "

    Even when Windows was shut down?

  17. Anonymous says:

    "Even when Windows was shut down?"

    Yes the registry stayed loaded even with the power off. You never know when you might need that registry in a power off scenario.

    Then again I really hope you were just kidding.

  18. Anonymous says:

    "

    Yes the registry stayed loaded even with the power off. You never know when you might need that registry in a power off scenario.

    "

    I meant: Was the registry base saved to disk when exiting Windows when a handle is leaking?

  19. anony.muos says:

    AFAI understand, with stupid Vista’s file type options removed, the only verb you can register for is "open" unless you do it programmatically. Of course, we’ve got Open with but that’s more than 2 clicks. Are we back to Windows 3.1 then?

  20. Anonymous says:

    "Please understand that this was not a problem in the 80’s.  Not everyone was a programmer.  There were not even formalized computer science degrees (in the 70’s up to the 80’s people who entered the programming profession usually had a degree in mathematics)."

    Where does this crazy rumor keep coming from?  There were well-established Computer Science degree programs when I started college in 1974.  Most of them had been around for years, certainly back into the 1960s.

    Some had close ties to Math, EE, or Systems Science programs, but they were clearly distinct from them.

  21. bcthanks says:

    This is going way off topic…

    "There were only a handful of programmers, most of whom were smart."

    And most users could be trusted.

    It’s funny people are defending their CS degrees. From the point of view of engineering reliable systems, Computer Science is as worthless today as it was back then.

    Having a CS degree does not make one a smart programmer. And it certainly does not confer any project management skills necessary to resist management’s push for more features in less time.

    Remember it was a bunch of academics who designed the C library, with gets() that doesn’t specify the buffer length, the unbounded strxxx functions and printf that writes back to its arguments.

  22. Anonymous says:

    bcthanks, that last line made my day.

Comments are closed.