Douglas A. Hamilton

WINDOWS NT

The Windows NT Registry

ERHAPS THE LEAST • Provide a secure interface controlling access to all of this information understood Windows • Allow configuration information to be shared across a network NT feature is the reg­ • Support a standardized user inter­ P face via Control Panel istry. What the registry Several of these objectives arise from the view of Windows NT as a system de­ does is gather together all signed for a networked environment with lots of machines and lots of users. the information that the The NT design team imagines a world in which you might sit down in front of needs to any machine on the network, log in and have it act indistinguishably from your know about the hardware own "home" machine. If you're the own­ er of the machine, it shouldn't be like having someone else drive your car, and (the type of processor, addresses of the peripheral con­ you find the seat's at the wrong height, the mirror's at the wrong angle and the trollers and so on), the system software (where the swap radio's on the wrong station. The result is a standardized database files are located, what time zone the machine is in, rec­ of information about your personal pref­ erences that can be shared in a secure ognized filename extensions and network protocols) manner across a network. With this in­ formation in the registry, as soon as you and each user (his or her preferred screen colors, envi­ log in, NT can go ask your own machine everything it needs to know about you. ronment variables and Program Manager choices). The No matter where you log in, it's just like home. And because it's secure, all that registry .also serves as a collection point for per-applica­ customization is still kept private, acces­ sible only to you even though it's avail­ tion information that otherwise would be scattered able to you anywhere you go. Internally, data is kept in the registry through myriad .INI files. in a tree structure rather like the file sys­ tem. To access any particular piece of in­ The objectives of the registry are to: formation you specify a , called a key, for traversing down the tree from • Overcome the confusion of so many different spe­ one of four predefined starting points. Individual leaves of the tree, called val­ cial files typically used to configure other systems ues, typically contain a name (thoug:h some values can be unnamed) plus the • Structure configuration information so that it can binary or character string data associated with that name. The four predefined be separated by system, user and application starting points are:

WINDOWS M AGAZINE • MAY 1993 225 Douglas A. Hamilton

Figure 1. setreg.:

#include "Environment", 0, KEY_READ, &h); HANDLE Stdout, Stderr; if ( == ERROR_SUCCESS) char*cmd; { DWORD cmdlen; rc = RegOueryValueEx(h, name, NULL, &type, void error( DWORD rc, char *msg) value, &valuelen); { if (rc == ERROR_SUCCESS) DWORD bytes; { WriteFile(Stderr, cmd, cmdlen, &bytes, NULL); WriteFile(Stdout, name, namelen, &bytes, WriteFile(Stderr, msg, strlen(msg), &bytes, NULL); NULL); i = namelen < 12? 13- namelen: 1; ExitProcess( rc); WriteFile(Stdout, spaces, i, &bytes, } NULL); DWORD DumpRegistry( void ) value[valuelen++] = '\r'; { value[valuelen++l = '\n'; /* Dump out all the environment variable:; WriteFile(Stdout, value, valuelen, shown in the registry. */ &bytes, NULL); HKEY h; } DWORD i, rc, namelen, valuelen, type, bytes; char name[MAX_PATH + 1]. value[4096 + 2]; return rc; rc = RegOpenKeyEx(HKEY_ CURRENT_ USER, "Environment", 0, KEY_READ, &h); DWORD SetVariable( char * name, char * value, if (rc == ERROR_SUCCESS) DWORD valuelen, DWORD type ) { for (i = 0; namelen = sizeof(name)- 1, HKEYh; valuelen = sizeof(value)- 2, DWORDrc; (rc = RegEnumValue( h, i, name, rc = RegOpenKeyEx(HKEY_ CURRENT_ USER, &namelen, NULL, &type, value, " Environment", 0, KEY _WRITE, &h); &valuelen)) == ERROR_SUCCESS; i++) if (rc == ERROR_SUCCESS) { rc ~ RegSetValueEx(h, name, 0, type, value, register char *t; valuelen); for (t = name + namelen; t < name + 12; return rc; *t++ = ' ') void main( void ) *t++ = ' '; WriteFile(Stdout, name, t - name, register char *c; &bytes, NULL); register DWORD rc; value[valuelen++] = '\r'; register BOOL got_equal; value[valuelen++] = '\n'; Stderr = GetStdHandle(STD_ERROR_HANDLE); WriteFile(Stdout, value, valuelen, if (Stderr == INVALID_HANDLE_VALUE) &bytes, NULL); ExitProcess(GetLastError()); } cmd = c = GetCommandLine(); if (rc == ERROR_NO_MORE_ITEMS) while (*c && *c != '' && * c != '\t') rc = 0; C++; cmdlen = c - cmd; return rc; while (*c == '' II *c == '\t') C++; DWORD DumpVariable( char * name, DWORD namelen ) Stdout = GetStdHandle(STD_OUTPUT_HANDLE) ; { if (Stdout == INVALID_HANDLE_VALUE) HKEY h; error( GetLastError(), char value[ 4096 + 2]; ": Couldn't open Stdout\r\n"); DWORD i, rc, valuelen = sizeof(value) - 2, if (*c == 0) type, bytes; /* Dump out all the environment variables static char spaces[12] = " "; in the registry. */ rc = RegOpenKeyEx(HKEY_CURRENT_USER, rc = DumpRegistry(); (Continued on page 227)

226 WINDOWS M AGAZINE • M AY 1993 Douglas A. Hamilton

(Continued from page 226) Figure 1. setreg.c:

else extra whitespace and watching for %'s. *I for (t = c; *c; *t++ = *c++) . register char * name = c; switch (*c) DWORD namelen; while (*c && *c !=' ' && *c != '\t' && *c != '=') inside_quotes = !inside_quotes; C++; C++; namelen = c- name; break; while (* c == '' II *c == '\t') case'\\': C++; if (c[1] == '\\' && if (got_equal = *c == '=') c[2] == "") { C++; I* Skip over any = followed by any break; spaces. *I case'': C++; case '\t': while (*c =='' II ·•c == '\t') if (! inside_quotes) C++; while (c[1] == ''II c[1] == '\t') if (*c == 0 && !got_equal) C++; I* Dump out just the one variable break; specified. *I case'%': rc = DumpVariable(name, namelen); type= REG_EXPAND_SZ; else } *t= 0; I* Set the variable to the specified name[namelen] = 0; value *I rc = SetVariable(name, value, t- value, register char *t, *value= c; type); register DWORD type = REG_SZ; } register BOOL inside_quotes =FALSE; I* First scan the value, fixing up any ExitProcess(rc); backslashes used as escapes, dropping }

• HKEY_LOCAL_MACHINE, which There are registry functions (Lookup­ best way to answer that is just to open contains information about the machine AccountName and LookupAccountSid) up the registry with RegEdit and try to itself and the hardware and software for converting back and forth between make your own guesses. that's been installed user names and SIDs, but HKEY_CUR­ Also, the user interface to the registry is • HKEY_USERS, which describes the RENT_USER lets you gain access to your meant to be via the applets in Control default environment for a new user and own custornization information without Panel. Clearly, not all the applets are fin­ the customized environments for exist­ having to do that translation between ished yet; for now, some changes can be ing users SIDs and names. made only with RegEdit. But make a • HKEY_CLASSES_ROOT, a synonym Admittedly, we are still looking at change incorrectly, and you've got a sys­ for HKEY _LOCAL_MACHINE \SOFT­ work-in-progress in the October beta, so tem that won't boot and can't easily be WARE\Classes, which lists all the known there are some rough spots (hopefully, by fixed; following one of my own failed ex­ file type extensions the time you read this, the next beta periments a few months back, I had to re­ • HKEY_CURRENT_USER, a synonym should be available and will most likely sign myself to a complete reinstallation. for HKEY_USERS\, where is a be more complete than this one). One Another problem with the Control long gobbledygook string giving the such rough spot is that, while the overall Panel applets is that they are only graphi­ unique security identifier (SID), rather architecture of the registry is fairly clear, cal. There are no provisions for manipu­ like a Social Security number, for a partic­ some of the details are not. For example, lating the registry from a batch file, for ular user. For example, on my what are the individual names of all the example- which brings us to a discus­ OlivettiiMIPS machine, my SID is S"1-S- nodes in the registry and just what infor­ sion about the programming interface to 21-242430772-281344-854032384-SOO. mation should go where? Right now, the the registry and a couple of tiny utilities

W INDOWS MAGAZINE • MAY 1993 227 Douglas A. Hamilton

Figure 2. unsetreg.c:

#include C++; #include if (* c == 0) HANDLE Stdout, Stderr; error(ERROR_INVALID_PARAMETER, char *cmd; ": Can't unset a nulllist.\r\n"); DWORD cmdlen; Stdout = GetStdHandle(STD_OUTPUT_HANDLE); void error( DWORD rc, char * msg ) if (Stdout == INVALID_HANDLE_VALUE ) { error(GetLastError(), DWORD bytes; " : Couldn't open Stdout\r\n"); WriteFile(Stderr, cmd, cmdlen, &bytes, NUILL); rc = RegOpenKeyEx(HKEY_ CURRENT_ USER, WriteFile(Stderr, msg, strlen(msg), &bytes, " Environment", 0, KEY_WRITE, &h); NULL); if (rc == ERROR_SUCCESS) ExitProcess(rc); { } register char * name, savech; void main( void ) do

HKEY h; name=c; register char *c; w hile (*c && *c != ' ' && * c != '\t') register DWORD rc; C++; Stderr = GetStdHandle(STD_ERROR_HANDLE); savech = *c; if (Stderr == INVALID_HANDLE_VALUE) *C++= 0; ExitProcess( GetLastError()) ; RegDeleteValue(h, name); cmd = c = GetCommandline(); } while (*c && * c !='' && * c != '\t') while (savech); C++; } cmdlen = c - cmd; ExitProcess( rc); while (*c =='' II *c == '\t') } for setting the environment variables setting a value. Also, if we're setting a Above, Figure 2 contains the compan­ you'll see every time you log in from the variable, we'll need to watch for %-style ion utility, called unsetreg.c. This utility command line or a batch file. references to other variables in the value deletes variables. Once again, the first Figure 1 contains setreg.c, a utility that we're giving it so we can tell the reg­ step is to open a handle to the environ­ used for setting an environment variable, istry those should be expanded later. ment key; unsetreg then iterates over the dumping a particular variable's value or Whether we're reading or writing to list of names it's given, calling dumping all the variables. For example, the registry, the first step is always the RegDeleteValue for each. to define the INCLUDE environment same: We have to open a handle to the When using setreg or unsetreg or any variable, type the following (the equal appropriate key with the desired access other registry function, do remember sign is optional unless you're setting a rights using RegOpenKeyEx. Here, we're that it's like making changes to CON­ variable to a null string): always opening the environment key in­ FIG.SYS under DOS: The changes don't setreg INCLUDE = c:\mstools\h side HKEY_CURRENT_USER. take effect immediately. Changes related To list all the variables currently de­ To dump the entire environment, we to an individual user will not take effect fined in the registry, type setreg. To print iterate calls to RegEnumValu e. Each call until you log out and then back in; the value of just a single variable, type returns a new name/type/value triple. changes related to the system as a setreg INCLUDE. We're ignoring the type here, assuming whole-for example, your device driver The code in the main procedure is that all the values will be printable strings: configuration- won't take effect until fairly standard stuff- just parsing the a good assumption for environment vari­ you reboot. • command line to figure out what you've ables but not one you can make in gener­ requested. I've chosen to get the com­ al. After all the values have been read, Douglas Hamilton is president of Hamilton mand-line text from GetCommandLine, RegEnumValue returns ERROR_NO_­ Laboratories (Wayland, Mass.) and author parsing out the punctuation that would MORE_ITEMS, indicating that we've suc­ ofth e Hamilton , an advanced inter­ be added if there were spaces or quotes cessfully read them all. To dump a specific active command processor and tools package embedded in the argument words; an al­ variable by name, we use RegQuery­ for OS/2 and Windows NT. Reach Douglas ternative would have been to use argv V

228 WINDOWS MAGAZINE • M AY 1993