An Excerpt from
Inside the Windows 95 Registry

by Ron Petrusha

(Edited by Andrew Schulman)

Registry Locking

The registry, as we've mentioned, is really a centralized database of system settings and application state information. In Chapter 1, we characterized it as "the glue that holds Windows together"-i.e., a critical structure in Windows' and its applications' operation. Windows itself is a very capable multitasking operating system. An examination of the voluminous output produced by RegSpy95 (which is discussed in Chapter 10) shows that Windows and Windows applications access information in the registry hundreds or even thousands of times per second.

Put all this together, and it's probably already occurred to you that, just as in any database application that is used intensively and that supports multiple processes, collisions are inevitable. At some point, one process is sure to attempt to read data that another process is busily writing. Or two processes eventually will attempt to update the same value, thereby thoroughly confusing and corrupting it. Recognizing this, you may even be reviewing the Win32 API to refresh your memory about functions like CreateMutex and ReleaseMutex.


In the event that you're unfamiliar with mutexes, they are objects used to protect a shared resource from simultaneous access by multiple threads or processes. Only one thread or process can have ownership of the mutex at any point, and only the thread or process with ownership of the mutex is allowed to access the resource guarded by the mutex.

Good news: internally, Windows 95 (or, more precisely, the Win95 Virtual Machine Manager) implements what appears to be robust locking to insure the integrity of registry data. A single mutex is used to guard the entire registry. Whenever Windows or an application writes, and in most cases when it reads, a registry key or a value entry, the VMM calls an ENTRY_LOCK procedure that includes a call to the VMM's _EnterMutex service. If the mutex is unowned when _EnterMutex is invoked, the calling thread takes ownership of it, and can access a particular registry resource. If the mutex is already owned, the calling thread is blocked until the owning thread calls an EXIT_UNLOCK procedure, which in turn calls the VMM _LeaveMutex service. If this locking fails during registry access, the registry function returns ERROR_LOCK_FAILED.

The following functions invoke the ENTRY_LOCK procedure:

To test the effectiveness of this scheme, we created two programs, the first a simple registry "writer" and the second a simple registry "reader." The writer program continuously wrote 16,364 bytes of REG_BINARY data (the maximum amount of data that can be stored to a single value) to a value entry. During each iteration of the loop, the memset function was used to assign each of these bytes the same value. After each iteration of the loop, that value was incremented by one. (Once the value of each byte reached 255, of course, it was reinitialized to 0.) The reader program meanwhile continuously retrieved the entry's REG_BINARY value, read its first byte, then reported if the value of any subsequent byte differed from the value of the first byte. In repeated tests, including one in which three instances of the writer and two instances of the reader application were running simultaneously, the reader application did not report a single divergent byte of data.

This suggests that read and write operations performed on a single registry key or a single value entry are atomic, very much like those in Windows NT. That is, barring some catastrophic failure (like a loss of power), the locking scheme insures that a particular operation is completed before some other operation on that key or value entry-indeed, on any part of the registry-is permitted.

However, it's important to recognize that a single thread owns the registry mutex only for the duration of its call to a particular registry API function; before each registry function exits, it calls an EXIT_UNLOCK procedure. For instance, the thread that calls RegOpenKey or RegOpenKeyEx takes ownership of the mutex, but only for the period that the function executes; the thread does not retain ownership of the registry mutex for the entire period that the key is open. (Otherwise, failing to call RegCloseKey would completely disable the entire system.)


The Registry Mutex and RegConnectRegistry

You might be wondering how the multithreaded REGCONN program is able to kill a thread while it's attempting to access a remote registry when this locking scheme is in place. The answer is that locking is implemented by the local VMM, but RegConnectRegistry is not implemented by calling down to the Virtual Machine Manager; instead, it calls WINREG.DLL, which does a remote procedure call (RPC) to REGSERV.EXE running on the remote machine, which calls down to its VMM (see Figure 4-1).


Multiple operations, thus, are not atomic. For instance, if your application uses RegEnumValue to enumerate all the values belonging to a particular key, a mutex is used to insure the integrity of the individual read operation. But the integrity of the enumeration as a whole cannot be guaranteed; that is, there is no guarantee that the properties of the value entries and the values themselves will be the same when you finish your enumeration as they were when you began your enumeration. In fact, since the registry's locking scheme is implemented at a system level by the Virtual Machine Manager, it is not possible to apply it to multiple operations, since the system has no way of knowing how many operations you intend to perform. Attempting to apply it to multiple operations would inevitably block other applications' access to the registry and bring the system to a crawl. Consequently, the documentation for RegEnumKey, RegEnumKeyEx, and RegEnumValue in the Win32 SDK warns that, while enumerating keys or values, your application should not modify any of the objects being enumerated.

So it is quite possible, when you're in the middle of a series of registry operations, that some other application can change either the data itself or information about the data (like the number of value entries belonging to a subkey or the number of bytes in the longest data value). For instance, before using RegEnumValue to enumerate a series of values, you call RegQueryInfoKey to determine both the number of value entries and the length of the longest data item. As you're enumerating the values, another application deletes a value that you haven't yet enumerated, and writes new data to another value entry that you haven't enumerated that is significantly longer than RegQueryInfoKey reported. As you'll see in the next section, "I Was Shocked to Discover That It's a Whole New Registry...," the absence of registry change notification messages or services means that you'll never be informed that anything has changed. Yet, your application undoubtedly is prepared to loop x times, until all keys have been read, and to store their data in a buffer of y bytes, which is just enough to accommodate the largest value entry. This means that your application will probably fail.

Given this potential problem, what can you do to insure that your application continues to function reliably? You have two alternatives:

I Was Shocked to Discover That It's a Whole New Registry...

The registry is a very dynamic structure. Both Windows itself and individual applications use it to store user information, application state information, system information, and interapplication information, among other things. As a result, the registry is continually changing. Typically, though, applications read most of the basic registry settings that they need just once, when the application is loading. And, of course, they are aware of changes to registry settings that they themselves make. But from the time that an application last retrieves data from the registry, any other application can also modify the settings that it has read. How, then, do Windows applications learn that a registry setting on which they're depending has changed?

In Win31, with its system of initialization files, the Windows messaging system was responsible for notifying running applications of changes to WIN.INI, the major software configuration file. If Windows or an application changed WIN.INI, it could use either the SendMessage or the PostMessage function to send a WM_WININICHANGE message to selected windows or to all windows. The syntax of both functions in the Win32 API is:

BOOL PostMessage( hWnd, Msg, wParam, lParam ) ;
BOOL SendMessage( hWnd, Msg, wParam, lParam ) ;
HWND hWnd Handle of the window to which the message is to be sent; if the message is intended for all open windows, its value should be HWND_BROADCAST (0xFFFF).
UINT Msg Code number for the message to post, in this case WM_WININICHANGE (0x1A).
WPARAM wParam The first message parameter; for WM_WININICHANGE, this parameter is not used and must be 0.
LPARAM lParam The second message parameter; for WM_WININICHANGE, it should be a pointer to a null-terminated string containing the name of the section in WIN.INI that was modified. A NULL value indicates that multiple sections of WIN.INI have changed.

In addition, the WM_WININICHANGE message could automatically be sent by the SystemParametersInfo function if it was used to modify the value of one of its predefined system settings in WIN.INI.


Notification and Atomic Operations

In the previous section, "Registry Locking," we saw that only the operation of single registry functions are automatically protected by the registry mutex; multiple operations are not automatically protected. So even when accessing the registry for a series of operations (like enumerating a key's subkeys or value entries), Windows cannot guarantee that the information you're accessing differs from the state of the registry when you began your operation. That makes it important that Windows provide some mechanism to let you know that the registry has changed, and that those changes may affect the integrity of your series of operations. Once you've accessed information in the registry, this same mechanism should inform you that settings have changed, and that you need to read the original information once more. This section explores Win95's failure to implement such a mechanism.


In short, applications that modified WIN.INI were supposed to send a message indicating which section of WIN.INI they had modified. Conversely, all applications that depended on settings in WIN.INI were supposed to include an option for handling the WM_WININICHANGE message in their window procedures. This involved determining whether the application relied on any entries in the modified section and, if it did, rereading the section's values. Finally, a window procedure that processed the WM_WININICHANGE message was to return a 0, indicating that it had successfully handled the message.

Unfortunately, though, this was more of an ideal than a reality. While generally applications did send messages notifying other applications that WIN.INI had changed, most applications failed to include the code to handle WM_WININICHANGE messages in their standard window procedures.

This lack of compliance with the standards for Win31 may have inspired the current state, in which there is no standard way (and, in most cases, no meaningful way at all) to let other applications know that particular values or a particular area of the registry has changed. One would expect, of course, that since Win95 has moved the bulk of configuration information from initialization files to the registry, the Win32 API should include a new message, like WM_REGCHANGE, whose parameters indicate which key and possibly which value entry has changed. But instead, the Win32 API does not include a message that indicates the registry has changed.

Rather than supporting a message to notify of registry changes, WinNT offers a function, RegNotifyChangeKeyValue. Its major purpose, though, is to allow for thread synchronization in multithreaded applications. And it is not implemented on Win95.

Instead, Win95 adds a new message, WM_SETTINGCHANGE, to the Win32 API. According to the Win32 SDK documentation, it completely supplants WM_WININICHANGE, which is no longer supported by Win95.

In contrast to WM_WININICHANGE, which was used to indicate any change to WIN.INI that might interest an application, WM_SETTINGCHANGE has a much more limited role: it indicates that one of the basic system parameters set by the SystemParametersInfo function has changed. This function both retrieves the state of individual system settings and allows a myriad of basic system options to be set, such as the dimensions of icons, whether a screen saver is enabled, or whether fast task switching (the use of Alt-Tab to navigate among tasks) is allowed. The last parameter of SystemParametersInfo defines the character of the changes: according to the documentation, a value of SPIF_UPDATEINIFILE makes the new value permanent by modifying the registry, while a value of SPIF_SENDWININICHANGE (sic) under Win95 presumably causes the function to send a WM_SETTINGCHANGE message. Since this message is implemented exclusively on Win95, WinNT continues to send the more traditional WM_WININICHANGE message.


The general syntax of SystemParametersInfo is:
BOOL SystemParametersInfo( UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinINI );
where: uiAction indicates the system parameter to query or set, and is generally represented by one of a vast array of constants; uiParam and pvParam provide additional information needed to change the setting indicated by uiAction; and fWinIni indicates whether modifications are to be made to the registry or to WIN.INI.

When the WM_SETTINGCHANGE message is sent, the wParam and lParam parameters to the SendMessage and the PostMessage functions are:

WPARAM wFlag Indicates which system parameter has been changed by the SystemParametersInfo function. This value is the same as the value supplied to the uiAction parameter of SystemParametersInfo.
LPARAM pszMetrics Points to a buffer containing the string "WindowMetrics" if the value of wFlag is SPI_SETANIMATION, SPI_SETNONCLIENTMETRICS, SPI_SETICONMETRICS, or SPI_SETMINIMIZEDMETRICS. Otherwise, its value is NULL.

An application that processes the WM_SETTINGCHANGE message should return 0 as the value of its window procedure.


SystemParametersInfo and MisInfo

Aside from the fact that its terminology (SPIF_UPDATEINIFILE and SPIF_SENDWININICHANGE) hasn't been updated, the behavior of the Win95 implementation of SystemParametersInfo differs from the behavior described in the documentation in two ways:


Interestingly, if we examine WINUSER.H, the header file that contains the definitions of Windows messages, we find the following lines:

#define WM_WININICHANGE    0x1A
#define WM_SETTINGCHANGE   WM_WININICHANGE

So in fact, Windows does continue to support WM_WININICHANGE. WM_SETTINGCHANGE is simply a syntactical variation on the standard version of WM_WININICHANGE: wParam, instead of being 0, now indicates the specific system setting modified by SystemParametersInfo. Given the value of wParam, it is very easy for the application that processes the WM_SETTINGCHANGE message to use the SystemParametersInfo function to retrieve the new setting by calling the constant used to retrieve a setting instead of the constant used to modify it. For example, if an application has modified the width of windows' sizing borders, the following code fragment in the application's window procedure retrieves the new value:

// Check for change using SystemParametersInfo
case WM_SETTINGCHANGE:
   switch (wParam)
      {
      case (SPI_SETBORDER):
         SystemParametersInfo( SPI_GETBORDER, &uintBorderSize, 0, 0 ) ;
          etc. 

The more conventional variation of WM_WININICHANGE, on the other hand, indicates which section of WIN.INI has changed. Given the very different structures of the registry and initialization files, in most cases this is of very little value in indicating which section of the registry has changed.


This is not completely true of the message under Windows NT if it applies to an initialization file setting that has been relocated to the registry. Since Windows NT supports initialization file mapping, the GetProfileString and GetProfileInt functions, which can be used to retrieve initialization file settings, will instead retrieve information from the registry.

Despite its lack of apparent utility, and despite the documentation's claim that WM_WININICHANGE is not implemented on Win95, Microsoft recommends that the WM_WININICHANGE message be used to indicate particular kinds of changes to the registry or to WIN.INI. Two of these special-purpose uses of WM_WININICHANGE include:

This lack of a standardized method of notifying applications of changes to system-wide settings-and frequently the lack of any method of notifying applications of changes-is a real shortcoming in Win95. There is a certain irony in this situation: On the one hand, Win95 aims at putting an end to the anarchy of Win3x, where far too many applications behaved as if they were the only programs on the user's desktop; on the other hand, one excellent means of doing this, which complements the other methods-like a reliance on OLE and the extensive use of the registry-would involve further developing the Windows messaging system. In failing to do this, Win95 in some sense represents a step backwards from the viewpoint of one application letting other applications know what it is doing to the system as a whole.


Inside the Windows 95 Registry