So far, all the chapters in this book have taught you how, and why, to use the Registry tools that Microsoft provides as part of Windows 2000. For the most part, these tools are sufficient for everyday use. However, you may find it necessary to write your own tools from time to time.
Windows 2000 also provides a comprehensive set of routines that allow your programs to read, write, and modify Registry keys and values. You can also connect to remote computers’ Registries, get and set security data on keys and values, and do basically everything that RegEdt32, RegEdit, and the resource kit utilities can do. This capability is a double-edged sword: you can write programs that do exactly what you want, but the burden of properly using the Registry calls is entirely on you.
The original Registry API is defined in
winreg.h, part of Microsoft’s Win32
Software Development Kit (SDK) for NT 4.0 and Windows 95. The current
version is still part of the Win32 API, but now it lives in the
Microsoft Developer Network (MSDN) Platform SDK. There are 28
distinct routines in the Registry API, though most of them actually
have two variants: one that works with standard one-byte ASCII
strings and another that handles Unicode strings. The ASCII versions
have routine names that end in “A,” such as
RegCreateKeyA
, while the Unicode versions end with
a “W,” as in RegCreateKeyW
. Macros in
winreg.h automatically map the correct variant
to the routine name. When you call RegCreateKey
,
you automatically get the correct Unicode or ASCII variant depending
on how your header files are set up. (Of course, in Visual Basic or
Perl this distinction is moot.) The Registry stores strings in
Unicode format, so when you call one of the ASCII variants, the
Registry code takes care of converting one encoding to another.
As if this original set of functions wasn’t enough, Microsoft has added a separate set of Registry-related API routines as part of the Internet Explorer 4.0/5.0 shell. These routines are delivered as part of the Shell Lightweight Utility API, and most of them are implemented in Version 4.71 and later of shlwapi.dll. All machines running Windows 2000 or 98 have this DLL (as of this writing, it’s Version 5.00), while machines running Windows 95 or NT 4.0 have it if they also have Internet Explorer 4.0 or later. Some functions discussed later in the chapter are only available as part of Internet Explorer 5.0 or later; those functions are noted.
If you’ve used any other set of Win32 API routines, you’ll probably find the Registry API easy to digest. If you haven’t, though, a brief review of some Win32 API fundamentals will help flatten your learning curve.
Each Registry routine described next has its
own unique set of parameters. These parameters give you a way to tell
the API routines what you want done and how to do it. It’s
important to make sure you specify the parameters completely and
correctly. If you don’t, you’ll likely get
ERROR_INVALID_PARAMETER
back as an error;
it’s entirely possible that instead you might get a corrupted
Registry and a crashed machine.
In general, the C/C++ declarations for the Registry routines use pointers both for input and output. For example, strings are always passed as pointers (surprise!), as are outputs for things like security attributes and newly opened HKEYs. The Perl and Visual Basic declarations use the type system appropriate for the language, as you’ll see in the sections that cover each language.
Every Registry API routine returns an error code as its value. These codes, all of which are defined in winerror.h, give you an easy way to test for success or failure of an operation. Table 8-1 lists the most commonly used codes. A few routines can return other error codes as noted, but these are the ones you’re most likely to see. Your code should always test for all returned errors (not just these) and handle them properly if they should occur.
Table 8-1. Registry Error Codes
Back in ancient times,[38] the original
Windows 3.x API was the One True API application developers were
counseled to use. As programmers did use the API, the inertia of a
large installed base made it hard for Microsoft to change the way any
of the original 3.x routines worked. Instead of changing the
originals, the Win32 API added new routines where necessary and gave
them new names ending with Ex. For example,
RegOpenKey
begat RegOpenKeyEx
,
which adds an options flag and a SAM access context--both of
which are specific to Win32.
In general, you should avoid using the original routines when an Ex equivalent exists. Most of the cool features of the Windows 2000 Registry (especially those related to security) aren’t available with the “classic” API. In addition, it’s possible that the old-style routines will stop being supported in future Windows versions. In a few cases it may make sense to use the old-style routine anyway; I’ve noted these exceptions where appropriate.
The whole point behind the Win32 API is that you can write programs that use a single API. As long as you stick with that API, your code should run on any Win32-compliant platform, whether it’s Win95 on Intel, WinNT on Alpha, Windows 2000 on Itanium, or WinCE on whatever CPU the HPC builder chose. You’re not supposed to have to care which underlying operating system is present. While this is a wonderful theory, it sometimes breaks down in practice. For example, many of the routines described here have slightly different behavior under Windows CE.[39] More importantly, some routines don’t work at all under Win95.
This may be too harsh an indictment. What really happens is that the
routines don’t fail, but they don’t do what they’re
supposed to; they just return ERROR_SUCCESS
. This
means that your code still executes under Win9x, but it may not do
what you intended it to. At present, there are only four routines
that behave this way under Win9x: RegRestoreKey
,
RegGetKeySecurity
,
RegSetKeySecurity
, and
RegNotifyChangeKeyValue
. If
your application uses any of these routines, be forewarned: you
won’t get back the data you expect when your code is run under
Win9x. Be sure to handle these cases gracefully (for example,
checking whether the SECURITY_DESCRIPTOR
returned
by RegGetKeySecurity
is valid before trying to use
it).
The same is true for the shell APIs I mentioned earlier: none of those APIs are supported under Windows CE, and they may have slight functional differences between Windows 2000/NT and 95/98.
One of Windows 2000’s biggest advantages over Win9x is its robust security architecture. Since the Win32 API is supposed to be common across Win9x, Windows 2000/NT, and Windows CE devices, you may have seen, and ignored, some of the Windows 2000-specific datatypes used in Registry API routines. These datatypes can be useful, so a quick introduction will help you get familiar with them. (Skip this section if you already know how to use these types.)
The Registry API uses many standard Windows datatypes such as
DWORD
and LPSTR
. However, there
are six datatypes that are fairly unfamiliar to most programmers who
haven’t yet written Windows 2000-specific code. Each is used in
at least one Registry call.
HKEY
The initial letter of this type should tip you off to what it is.
Microsoft uses Hungarian notation,[40] so the initial H means this datatype is a handle to
something. An HKEY
is an opaque handle to a
Registry key; the handle actually points to a large table of key
references, so it’s not a handle in the pointer-to-a-pointer
sense most programmers usually use.
winreg.h includes definitions for the standard
six root keys. Anywhere you can use an HKEY
, you
can use HKEY_LOCAL_MACHINE or one of the other predefined root key
HKEY
s.
REGSAM
REGSAM
is really a DWORD
in
disguise; its values represent the permission you’re requesting
when you open or create a key. Legal values are shown in Table 8-2. You can use any of them when creating or
opening a key, but you should limit what you ask for to what you
actually need. In most cases, that means either
KEY_READ
or KEY_WRITE
.
Table 8-2. REGSAM Access Mask Values
SECURITY_INFORMATION
Windows 2000 allows you to read and write ACLs on Registry keys.
However, you must specify exactly which ACL you want to view or
change. The SECURITY_INFORMATION
type handles
this; it allows you to specify any of the values listed in Table 8-3 when calling
RegGetKeySecurity
or
RegSetKeySecurity
. The first four values in the
table are valid for Windows NT 4.0 or 2000; the last four are Windows
2000-only.
Table 8-3. SECURITY_INFORMATION Values
SECURITY_DESCRIPTOR
Access control data is stored in
SECURITY_DESCRIPTOR
structures. Like
HKEY
, HWND
, and other types, a
SECURITY_DESCRIPTOR
is opaque; there’s no
way to decipher exactly what it points to or contains without using
the Win32 security API routines. (Actually, this is a fudge.
Microsoft documents the structure but sternly warns developers
against reading or modifying its fields.)
SECURITY_ATTRIBUTES
The SECURITY_ATTRIBUTES
structure encapsulates a
security descriptor and data needed to interpret it:
typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES;
The nLength
member specifies the size of the
security descriptor pointed to by
lpSecurityDescriptor
. The
bInheritHandle
member controls whether a child
process spawned by the process that owns the
SECURITY_ATTRIBUTES
structure should also receive
the owning process’ security descriptor.
FILETIME
The FILETIME
structure contains the access date
and time for an object. Its format is a little odd:
typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME;
Together, the two DWORD
s represent the number of
100-nsec intervals since 1 January 1601. I have no idea what
possessed Microsoft to use this particular date as the base of their
time system. Fortunately, there are a number of routines for
converting between FILETIME
values and more useful
formats; check out FileTimeToSystemTime
for one
example.
When Microsoft added the shell utility routines as part of IE 4.0,
they also had to create some new datatypes to fully support those
routines. Most of the shell utility routines provide functionality
not included in the standard Win32 API set. However, the file
association routines (AssocCreate
,
AssocQueryKey
,
AssocQuery-String
, and
AssocQueryStringByKey
) bundle several Registry
operations into a single function. These routines actually
encapsulate the IQueryAssociations
COM object; its
purpose is to return the correct key and OLE class information from
HKCR for a specific type of document file. By providing a standard
way to do this (instead of requiring every developer to roll their
own) Microsoft is trying to reduce the number of association-related
frustrations foisted on end users. The new datatypes are:
ASSOCF
The ASSOCF
structure holds flags that specifies
what data you want back from a call to one of the association
functions. Table 8-4 shows the flags and their
values.
Table 8-4. ASSOCF Values
ASSOCKEY
The ASSOCKEY
enumerated type tells the association
routines what kind of key you want returned from your association
query. You have to use this type in calls to
AssocQueryKey
to ensure that you get the desired
key in return. See Table 8-5 for the
enumeration’s values.
typedef enum { ASSOCKEY_SHELLEXECCLASS = 1, ASSOCKEY_APP, ASSOCKEY_CLASS, ASSOCKEY_BASECLASS, } ASSOCKEY;
Table 8-5. ASSOCKEY Values
ASSOCSTR
The ASSOCSTR
enumerated type tells the association
routines what type of string you want as the result of a query. For
example, you can request the friendly name of an executable or
document type, the command for a particular shell verb, and so on.
Table 8-6 enumerates this type’s values and
their meanings.
typedef enum { ASSOCSTR_COMMAND, ASSOCSTR_EXECUTABLE, ASSOCSTR_FRIENDLYDOCNAME, ASSOCSTR_FRIENDLYAPPNAME, ASSOCSTR_NOOPEN, ASSOCSTR_SHELLNEWVALUE, ASSOCSTR_DDECOMMAND, ASSOCSTR_DDEIFEXEC, ASSOCSTR_DDEAPPLICATION, ASSOCSTR_DDETOPIC } ASSOCSTR;
Table 8-6. ASSOCSTR Flags
HUSKEY and PHUSKEY
HKEY
is an opaque type that represents a handle to
an open Registry key. HUSKEY
is a little
different. It’s a handle that represents a user-specific key
(as you’ll see in the next section).
Windows NT 3.1 introduced the concept of multiple user profiles to the Windows world. The idea was that each user could have her own group of personal settings that would automatically be loaded when she logged on. In Windows NT 3.51, Microsoft expanded this concept to cover domains, so that users could get their personal setting (or profile) information no matter where in the domain they logged on. However, some applications store their settings under HKCU, and others use HKLM. Compounding the problem, not all programs and components keep their setting data in the Registry. The introduction of user-specific class keys (see HKEY_CLASSES_ROOT) makes things even more complicated, since some per-user settings may actually be inherited from HKCR.
To fix this problem, Microsoft has introduced the concept of user-specific keys (USK). The idea is that all settings for one user can be stored beneath that user’s USK, which then conveniently becomes the user’s profile, making the settings portable. Applications that use the shell utility API are encouraged to use the USK functions to store and retrieve user-specific data so that all the user’s profile settings are stored in the same place.
Almost every C or C++ book includes an example based on the famous “Hello, World” example from Kernighan and Ritchie’s The C Programming Language. Following that venerable tradition, Example 8-1 shows what a similar program that uses the Registry looks like.
Example 8-1. A Modern Variation of the Canonical “Hello, World” Program
#include <windows.h> #include <winreg.h> #include <stdio.h> // Hello, World! for the Registry: gets this machine's name and prints // it out. void main(void) { unsigned char pszName[MAX_PATH] = ""; DWORD nNameLen = MAX_PATH; HKEY hkResult, hStartKey = HKEY_LOCAL_MACHINE; long nResult = ERROR_SUCCESS; nResult = RegOpenKeyEx(hStartKey, "SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName", 0L, KEY_READ, &hkResult); if (ERROR_SUCCESS == nResult) { nResult = RegQueryValueEx(hkResult, "ComputerName", 0, 0, pszName, &nNameLen); if (ERROR_SUCCESS == nResult) printf("Hello, world, from %s! ", pszName); else printf("I don't even know my own name. "); } RegCloseKey(hkResult); }
Throughout the C examples in this section, you’ll notice that I’ve had to use double backslashes (\) in Registry paths. That’s because the C preprocessor treats a single backslash as a flag character that marks a special character sequence; to get one backslash in a string, you need to include two.
This example contains code to implement the three most basic--and most common--Registry operations:
Open a key whose full path you know using
RegOpenKey
or RegOpenKeyEx
,
then retain the HKEY
returned when the key is
opened.
Use that returned HKEY
to get a value whose
location and type you already know (in this case,
HKLMSYSTEMCurrentControlSetControlComputerNameActiveComputerName).
Do something with the retrieved value, and close the key opened in Step 1.
Almost all programs that use the Registry involve these three steps. Of course, in addition to (or instead of ) reading Registry data, you can write new data to a value or enumerate a sequence of keys or values to find one that matches what you’re looking for. You’ll learn how to do all these things in the following sections.
In Chapter 1, I pointed out the organizational similarities between a filesystem and the Registry. These similarities are more than skin deep: they extend to the actual process of moving data into and out of the Registry. In general, the same rules apply when working with Registry keys and their values as with disk files.
First and foremost, you have to open a key when you want to use it, then close it when you’re done. If you don’t open a key, you can’t get its values. If you don’t close the key when done, other applications can’t access it, and your changes aren’t written out when you’d expect them to be. The API routines that open keys require two arguments: a path to the key you want to open and an open parent key. This may seem like a Catch-22: how can you open a key if you must already have an open key? The answer is simple: the root keys (HKLM, HKCC, HKCU, HKU, HKDD, and HKCR) are always open, so you can use them when you open any other key.
There are exceptions to the foregoing rule: some of the shell utility
API routines don’t have to open or close keys. For example, you
can call SHRegCreateKey
, which creates a new
user-specific key underneath your choice of HKLM or HKCU, without
opening either parent key. You even get back a handle that you can
use with other shell API routines, all without opening or closing any
other keys.
The next similarity involves access controls and rights. If you’re accustomed to NTFS, Unix, or Novell filesystems, you know that files and directories can have permissions attached to them that govern who can open, modify, delete, and move things around. In ACLs, files also have rights, which the ACLs grant. One entry in the ACL might grant Administrator the right to read or write a file, while another might deny write access to members of the Domain Users group. Registry keys have these same controls and rights. As you’ll learn in Chapter 9, you can keep your Registry secure by putting ACLs on security-sensitive keys. When you open a Registry key, you must specify what access you want to it: read, write, enumerate, and delete are all examples. Windows 2000 checks the access you request against the ACLs on the Registry key to decide whether or not you get access.
The best way to stay out of trouble when opening and closing keys is
to remember to balance key openings with closings. Later in the
chapter (in Example: A Stack-Based Wrapper Class),
you’ll see a C++ class,
StKey
, that automates the cleanup process.
Please be sure to close any keys you open even when errors or
exceptions interrupt the normal flow of control in your
code.
When you’re ready to open a key, there are two different
approaches you can take. The first one is to use the
RegCreateKey
or RegCreateKeyEx
functions, which I’ll talk
about in a bit. They’ll automatically open the key you specify
or create it if it doesn’t exist. The second method, which is
probably better for most applications, is to open the key with
RegOpenKeyEx
or
RegOpenKey
. Why are these calls better? They fail
when you try to open a key that doesn’t exist, while the
RegCreate
functions will create a new key with no
values in it. Imagine that you’re calling a friend named Bill
on the phone. If you call and are told “Bill’s not
here” by the person who answers, that’s the equivalent of
calling RegOpenKey
routines on a nonexistent key.
By contrast, calling Bill and being told “Bill’s not
here, but I’ll pretend to be him” is more or less what
happens when you call RegCreate
. That may
sometimes be desirable, but it’s not a pleasant surprise if
you’re not expecting it.
The recommended way to open a key is with
RegOpenKeyEx
. You supply an open key, which may be
a root key or a key you’ve already opened; the name of the full
path to the key you want to open; and a mask describing what access
you want to the newly opened key.
LONG RegOpenKeyEx(hKey, pszSubKey, dwOptions, samDesired, phkResult);
The following code opens a key under HKLM
for
reading, then goes on to do some other processing (which I’ve
omitted here). If you combine the root key and the value of
pszSubKey
, you’ll see that the key being
opened is HKLMSOFTWARELJLArmorMailUsers; if I’d already had
any key in that path open (for example, HKLMSOFTWARELJL) I could
have shortened the subkey name accordingly.
DWORD result = ERROR_SUCCESS; HKEY firstKey; // try to open the user list key; if we succeed, enumerate its subkeys result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\LJL\ArmorMail\ Users", 0L, KEY_READ, &firstKey); if (ERROR_SUCCESS == result) ...
If you try to open a key for access that the DACL on the key
doesn’t allow (for example, trying to open any of the
HKLMHARDWARE subkeys for write access from an unprivileged user
account), you get ERROR_ACCESS_DENIED
for your
trouble. One of the “strongly recommended” criteria for
getting the Win9x and Windows 2000 certification labels is that you
should open keys with the privileges you need: don’t ask for
KEY_ALL_ACCESS
when what you really need is
KEY_READ
. You should ask for write access only
when you’re ready to write data to the Registry; this reduces
the risk that your code will accidentally damage the Registry while
you’re developing it.
If you’re willing to use the default system security mask for
key access, you can use the RegOpenKey
function
instead. It takes the same hKey
,
pszSubKey
, and phkResult
parameters as RegOpenKeyEx
, but it doesn’t
accept a desired SAM mask.
LONG RegOpenKey(hKey, pszSubKey, phkResult);
The only difference between RegOpenKey
and
RegOpenKeyEx
is that the latter has two extra
parameters. Apart from that, they function identically. One
portability warning, though: as with the other Win 3.x Registry API
calls, RegOpenKey
is unsupported on Windows CE. If
you’re writing code you want to be portable, stick with the
.Ex functions, tempting though the old ones may
be.
As it turns out, Windows NT and 2000 both cache the contents of HKCU for all threads in a process. This is a big efficiency win (which is why Microsoft did it), but if you’re writing an application that uses multiple threads, it can pose a sticky problem if any of those threads has to impersonate another user. For example, let’s say you’re writing an antivirus utility. You want it to be able to scan memory and files owned by whichever users are present on the system, so you code it to spawn one thread for each interactive or network user. Guess what? The default behavior results in your application reading, and storing, settings only in HKCU, even if other users have set preferences in their own profiles. This problem is particularly acute for people who are writing management utilities that have to deal with users and services sharing a computer (or, worse, using Terminal Services).
There’s a way to fix this when writing applications for Windows
2000: the
RegOpenCurrentUser
call opens the appropriate user-specific key for the thread that
calls it. For example, if you have one thread running as
Administrator
and another running as
RApaulr
, and each thread calls
RegOpenCurrentUser
, one thread gets
HKUAdministrator and one gets HKUpaulr.[41]
LONG RegOpenCurrentUser(rDesiredPerms, phkResult);
In Windows 2000, the class information that used to live only in HKCR has been partitioned into two chunks: one that lives in HKCR and one that occupies the new, user-specific HKCUClasses subkey. When you want data about OLE/ActiveX objects or class definitions (say, to find out which class factory to use to create a new object), how do you know where to look? Worse still, what if you’re writing a multiuser or server-based application that needs to get the correct settings for whatever user is currently making a request? Oh, the horror.
The solution is a new, Windows 2000-only API call,
RegOpenUserClassesRoot
.
This routine allows you to open a handle to the class data for a
particular user. Windows 2000 automatically combines that
user’s HKCUClasses key with the machine’s HKCR data to
present a single unified tree to your program.
LONG RegOpenUserClassesRoot(hToken, dwOptions, samDesired, phkResult);
The dwOptions
, samDesired
, and
phkResult
parameters are all pretty
straightforward, since they work the same as they do when calling
RegOpenKeyEx
. hToken
takes a
little more explaining: it’s a process token like the one the
system generates internally when you log on interactively. In fact,
you can pass that same token to
RegOpenUserClassesRoot
, but normally you
wouldn’t need to, since you can get the active user’s
class data when running processes in that user’s context.
It’s more likely that you’d need to get a token
representing some user other than the current user. For example, in a
multiuser server application, you’d probably want to retrieve
each individual user’s data by opening their class data key.
There are six routines that can give you back a token of the type you
need to call RegOpenUserClassesRoot
: (see Table 8-7). Which of these routines you use will depends
on what you’re trying to do. Most of the time, though,
you’ll probably use either LogonUser
,
OpenProcessToken
, or
OpenThreadToken
.
Table 8-7. API Routines That Can Give You a Token to Use with RegOpenUserClassesRoot
API routine |
Use it when you want to... |
---|---|
|
Log a new user on to the local computer and run processes as that user. |
|
(Windows 2000 only) Create a new token with fewer privileges than some existing token. |
|
Duplicate an existing token, keeping the same access privileges. |
|
Duplicate an existing token, creating either an exact duplicate or an impersonation token. |
|
Obtain a handle to the access token of an existing process. |
|
Obtain a handle to the access token of an existing thread. |
There’s
only one way to close a handle to a key:
RegCloseKey
. You pass in the
HKEY
you want to close. If it’s successfully
closed, you get ERROR_SUCCESS
back. Otherwise, you
get an error that indicates what went wrong.
LONG RegCloseKey (hKey);
You can actually call RegCloseKey
on one of the
predefined root key entries. It reports a successful close but
doesn’t actually close the root key. This frees you from
worrying about whether the HKEY
you’re
trying to close is really yours or not.
When you close an HKEY
, any data you’ve
changed in that HKEY
or its subkeys may be written
to disk. On the other hand, it may not; the Registry support code may
cache these changes until the next time it’s convenient to
flush them out to disk. Don’t assume that your changes are
immediately preserved as soon as you close a HKEY
.
Do assume that your changes are not preserved until you do
so.
You can create new keys anywhere you have permission. As I pointed out in earlier chapters, you probably won’t need to do so very often unless you’re writing applications that use the Registry to store their parameters. Just in case, though, here’s how to do it.
RegCreateKeyEx
is the most powerful function for creating a new key. When you ask it
to create a key, it does so, then opens it. If the key already
exists, it just opens it and returns a handle to it. In either case,
after a successful call to RegCreateKeyEx
you’ll have an open key handle that can be used for all manner
of things as described elsewhere in the chapter.
LONG RegCreateKeyEx(hKey, pszSubKey, Reserved, pszClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
HKEY |
hKey |
Handle to an open key under which the new subkey is created; applications can’t create keys directly under HKLM or HKU. |
LPCSTR |
pszSubKey |
Path to the new subkey you want to create; this path is interpreted
relative to |
DWORD |
Reserved |
Reserved; must be |
LPCSTR |
pszClass |
Specifies the class of the key. Microsoft says “No classes are
currently defined; applications should pass a |
DWORD |
dwOptions |
May be |
REGSAM |
samDesired |
Contains an access mask specifying what access you want to the new key; see Table 8-2 for a complete list. |
LPSECURITY_ATTRIBUTES |
lpSecurityAttributes |
On input, points to a |
PHKEY |
phkResult |
Pointer to |
LPDWORD |
lpdwDisposition |
Points to a |
When you open an existing key, RegCreateKeyEx
ignores the lpClass
, dwOptions
,
and lpSecurityAttributes
parameters, since their
values are determined by the existing key.
Once you successfully call RegCreateKeyEx
,
you’re guaranteed to have an open HKEY
you
can use to add values or subkeys. Of course, a newly created key
won’t have any of either item, but an existing key that
RegCreateKeyEx
opened might indeed; be sure to
check lpdwDisposition
if you need to know whether
the key was created or just opened.
You can use RegCreateKeyEx
as a mutual-exclusion
locking mechanism (or mutex) for two or more processes. When one
process creates a new key using RegCreateKeyEx,
the return value is REG_CREATED_NEW_KEY
. When
subsequent processes try to create the same key, they get back
REG_OPENED_EXISTING_KEY
, which they can use as a
signal that the mutex is in use. Windows NT and 2000 offer more
sophisticated mutex mechanisms, but this one has the advantage that
it works on any variant of the Win32 API--even under emulators
like Linux’s Wine.
You can also use the less-flexible
RegCreateKey
,
but neither Microsoft nor I recommend it. It lacks a way to specify
what access or security attributes you want to apply to the key,
meaning that it may fail unexpectedly when trying to open an existing
key that has an ACL applied to it. In addition, it doesn’t tell
you whether it created a key or opened it.
LONG RegCreateKey(hKey, pszSubKey, phkResult);
Every key has a great deal of information associated with it, even if it’s not immediately obvious. When you use one of the Registry editing tools, you see a neatly tree-structured view of what’s beneath each root key, but the system maintains a lot more data beneath the surface so that it can efficiently access keys and values and give them back to requesting programs.
RegQueryInfoKey
gives you access to a total of 11 different pieces of data for any
key in the Registry. Typically you use it to find how many subkeys or
values exist so you can efficiently enumerate through them (more on
that in the next section). RegQueryInfoKey
looks
like the following.
LONG RegQueryInfoKey(hKey, pszClass, lpcbClass, lpReserved, lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor, lpftLastWriteTime);
HKEY |
hKey |
Handle to any open key or root key. |
LPTSTR |
lpClass |
Points to a buffer that receives the key’s class name. May be
|
LPDWORD |
lpcbClass |
Points to a |
LPDWORD |
lpReserved |
Reserved; must always be |
LPDWORD |
lpcSubKeys |
Points to a |
LPDWORD |
lpcbMaxSubKeyLen |
Points to a |
LPDWORD |
lpcbMaxClassLen |
Points to a |
LPDWORD |
lpcValues |
Points to a |
LPDWORD |
lpcbMaxValueNameLen |
Points to a |
LPDWORD |
lpcbMaxValueLen |
Points to a |
LPDWORD |
lpcbSecurityDescriptor |
Points to a |
PFILETIME |
lpftLastWriteTime |
Points to a |
Any of the parameters except hKey
can be
NULL
; if you specify NULL
for a
parameter, that data isn’t returned. Here’s a small
routine that gets the number of values attached to any open Registry
key; notice that it passes NULL
for everything
except lpcValue
:
DWORD GetKeyValueCount(HKEY inKey) // Gets the count of values attached to a particular key. Returns // the value count (which may be 0) or -1 if an error occurs. { DWORD valCount = 0; DWORD result = ERROR_SUCCESS; result = RegQueryInfoKey (inKey, NULL, NULL, // class & class size NULL, // reserved NULL, // # of subkeys NULL, // subkey length NULL, // class length &valCount, // # of values NULL, NULL, NULL, NULL); if (ERROR_SUCCESS != result) valCount = -1; return valCount; }
It’s worth making special mention of
lpcSubKeys
, lpcValues
,
lpcbMaxValueNameLen
, and
lpcbMaxValueLen
. It’s often necessary to do
some kind of processing over every key or value under a particular
subkey. This enumeration is nothing more than an
iterative loop that starts with the first key or value of interest,
then proceeds on, continuing until it has processed every key or
value. For example, you could enumerate the subkeys of HKU to find
out the SIDs of every installed local account on a machine. Armed
with that information, you can look up the account names to build a
list of users who have profiles on the machine.
These four parameters make it easier to efficiently enumerate keys and values. Knowing how many items there are makes it possible to enumerate any subset of a key’s values, and knowing the maximum name and content lengths means you can allocate a buffer that’s just the right size, instead of too big or too small, to hold the data returned by the enumeration.
The enumeration API routines treat a key’s subkeys or values as an ordered list of n values, numbered from to n-1. You pass an index value to the API routines to indicate which key or value you want; the corresponding key or value is returned. For values, there’s an extra wrinkle: keys can have a default value, which always appears as item in the enumeration list. (You’ll see how this works in Enumerating values later in this chapter.) This is convenient, but don’t be misled: the values or keys aren’t really an ordered list, and if you enumerate the same subkey twice in a row, you can potentially get items back in a different order each time.
When you enumerate keys or values, there are a few different
strategies you can use to process all the enumerated keys. The
easiest way is to call
RegQueryInfo-Key
to find out how many subkeys or values
exist, then use a simple loop to process every key or value. A small
snippet implementing this tactic might look like:
DWORD idx=0, keyCount = 0 LONG retVal = 0; retVal = RegQueryInfoKey (inKey, NULL, NULL, // class & class size NULL, // reserved &keyCount, // # of subkeys NULL, // subkey length NULL, // class length NULL, // # of values NULL, NULL, NULL, NULL); for (idx=0; idx < keyCount; idx++) { // get the idx'th key's name and length retVal = RegEnumKeyEx(interestingKey, idx, name, nameLen, NULL, NULL, NULL, NULL); // do something with it }
This approach has the advantage of being simple to implement and
understand. However, you may not want to process every key or value.
Instead, if you want to process only keys or values that meet some
criterion, you can use a conventional while
loop
like this:
DWORD idx = 0; bool keepGoing = true; LONG retVal = 0; while (keepGoing) { retVal = RegEnumKeyEx(interestingKey, idx++, name, &nameLen, (unsigned long *)NULL, (char *)NULL, (unsigned long *)NULL, (LPFILETIME)NULL); if (ERROR_SUCCESS == retVal) { // If we're interested in this key, we'd process it further; // we might also set keepGoing here if we only want one key } keepGoing = (keepGoing && retVal == ERROR_SUCCESS); }
With this approach, you don’t have to know in advance how many keys or values exist, and it’s a simple matter to stop enumerating as soon as you find what you’re looking for.
You enumerate keys using the
RegEnumKeyEx
and RegEnumKey
routines. They’re very
similar; the primary difference is that
RegEnumKeyEx
allows you to retrieve the
modification time and class name for a subkey, while
RegEnumKey
doesn’t. In either case, you
simply supply the HKEY
you want enumerated and an
index value that indicates which subkey you want to see. The name
(not the complete path) of the corresponding subkey is returned, so
you can open any subkey you find by passing the name to
RegOpenKey
or RegOpenKeyEx
.
LONG RegEnumKeyEx(hKey, dwIndex, pszName, lpcbName, lpReserved, pszClass, lpcbClass, lpftLastWriteTime);
RegEnumKey
is identical in function, except for
having fewer parameters.
LONG RegEnumKey(hKey, dwIndex, pszName, cbName);
Once
you’ve located a key of interest, you might want to enumerate its
values. Most Registry keys have at least one value; quite a few have
many values whose number and contents vary from machine to machine.
(HKCR is a good example, because it differs depending on what classes
and objects are registered on a machine.) You can accomplish this
with RegEnumValue
: [42]
LONG RegEnumValue(hKey, dwIndex, pszValueName, lpcbValueName, lpReserved, lpType, lpData, lpcbData);
To see RegEnumValue
in action, check out Example 8-7 in Example: Loading a Control with a Set of Values later
in this chapter; the example illustrates the basic things you should
do when enumerating a set of values:
Call RegQueryInfoKey
first to get the maximum
subkey length, then use that to allocate any buffers you need to get
the value name or contents.
Make sure you either check for ERROR_NO_MORE_ITEMS
or honor the number of values returned by
RegQueryInfoKey
.
Open the parent key with KEY_READ
or
KEY_QUERY_VALUE
access.
Maybe you patiently enumerated a sequence
or keys, or perhaps you already know just where the data you want is
stored. Either way, at some point you’ll want to actually
retrieve a value stored under some Registry subkey. If you used
RegEnumValue
, you could have gotten the
value’s contents when you enumerated it, but if you just want
to grab a single value whose path you know, there are better ways for
doing so.
The first, and most useful, method of getting a single value’s
contents out of the Registry is the
RegQueryValueEx
function. As its name implies, it’s a Win32 routine; you supply
an open key and a value name, and it returns the value’s
datatype, length, and contents.
LONG RegQueryValueEx(hKey, pszValueName, lpReserved, lpType, lpData, lpcbData);
The most straightforward way to call
RegQueryValueEx
is just to get the value, like
this (assuming you’re fetching a REG_DWORD
value named “SomeValue” from a previously opened key):
nResult = RegQueryValueEx(hOpenKey, "SomeValue", NULL, NULL, (LPBYTE)&theValue, &valSize);
Since you always know how big a DWORD
is, the size
really isn’t important. Things get a little more complex when
querying for a string value named “SomeStringValue”. At
runtime, you don’t know the string’s length, which means
you must either dynamically allocate a buffer or check to see whether
there’s more data available than your buffer can hold.
RegQueryValueEx
returns
ERROR_MORE_DATA
if the requested value has more
data than can fit in the buffer length as specified by
lpcbData
:
DWORD bufSize = MAX_PATH; char theBuf[MAX_PATH]; nResult = RegQueryValueEx(hOpenKey, "SomeStringValue", NULL, NULL, (LPBYTE)theBuf, &bufSize); if (ERROR_MORE_DATA == nResult) { // too much data for our buffer; fail, use another buffer, or do // something else } else if (ERROR_SUCCESS == nResult) { // continue normally }
Alternatively, you can find out how big the value is, then allocate the buffer for it. This approach requires an extra Registry query but lets you economize on memory by not allocating any more than you actually need:
DWORD bufSize = 0; char *theBuf = NULL; nResult = RegQueryValueEx(hOpenKey, "SomeStringValue", NULL, NULL, NULL, &bufSize); if (ERROR_SUCCESS == nResult) { theBuf = (char *)malloc(bufSize+1); // allow extra byte for NULL // terminator if (theBuf) { nResult = RegQueryValueEx(hOpenKey, "SomeStringValue", NULL, NULL, (LPBYTE)theBuf, &bufSize); if (ERROR_SUCCESS == nResult) // do whatever with the value free(theBuf); } }
Notice that this code snippet adds an extra byte to the buffer to
allow for the NULL
terminator, which may be stored
as part of the string. Also notice that extra space isn’t
allocated for a Unicode string: if you define
UNICODE
, the initial call returns the
string’s Unicode length in bufSize
, but if
UNICODE
isn’t defined, the string is
converted into ANSI, and bufSize
contains the ANSI
string length.
The MSDN documentation for RegQueryValueEx
points
out that it can return things you didn’t ask for in some cases.
In particular, if you use RegQueryValueEx
to query
a value under HKEY_PERFORMANCE_DATA
, the data you
get back in lpData
may contain some extraneous
data, so you have to walk through the value’s contents yourself
to see what’s in it.
You can also use
RegQueryValue
to request a key’s value, but it can get only the default value
(remember, that’s the only value Win3.x supports, and
RegQueryValue
is a 3.x compatibility function).
LONG RegQueryValue(hKey, pszSubKey, pszValue, lpcbValue );
You can retrieve multiple values from a key at once using
RegQuery-MultipleValues
, but
its interface can be a little confusing.
LONG RegQueryMultipleValues(hKey, valList, numVals, pszValueBuf, ldwTotalSize);
To use this function, fill out an array of VALENT
structures: you put the value name you’re looking for in
ve_valuename
, and
RegQueryMultipleValues
fills in the other fields
for you:
typedef struct value_entA { LPSTR ve_valuename; DWORD ve_valuelen; DWORD ve_valueptr; DWORD ve_type; }VALENTA, FAR *PVALENTA;
On entry, pszValueBuf
should point to a buffer big
enough to hold all the value data you’re requesting. On return,
you can iterate through valList
; each item’s
ve_valueptr
member points to the location
within pszValueBuf
where the value data’s actually stored. You can also
call RegQueryMultipleValues
with an
pszValueBuf
of NULL; when you
do, ldwTotalSize
contains the buffer size required
to hold all the requested values.
Keys can signify things based on their
presence or absence, but values are the best way to store persistent
data in the Registry. The RegSetValueEx
function does double duty; it can create new values or change the
contents of existing ones.
LONG RegSetValueEx(hKey, pszValueName, Reserved, dwType, lpData, cbData);
HKEY |
hKey |
Points to any currently open key or one of the root keys. The key
must be opened with |
LPCSTR |
pszValueName |
Name of the value to set; if no value with the specified name exists,
|
DWORD |
Reserved |
Unused; must be 0. |
DWORD |
dwType |
Type of the value you’re adding or modifying; may be any of the types defined in Chapter 2. |
CONST BYTE * |
lpData |
Data to load into the value. |
DWORD |
cbData |
Length (in bytes) of the data pointed to by
|
If you call RegSetValueEx
with the name of an
existing value in pszValueName
, its contents and
type is replaced by whatever you pass in. If no such value exists,
it’s created with the contents and type you specify.
In addition to RegSetValueEx
, there’s also a
second value-setting function you may use:
RegSetValue
. It was originally part of the Win 3.1
Registry API. You may remember from Chapter 1 that
the Win 3.1 Registry allowed only a single value for each key. In
keeping with that heritage, RegSetValue
allows you
to set only the default value for a key, and the value you set must
be a REG_SZ
. I present this function for
completeness, but you should avoid it in favor of
RegSetValueEx
.
LONG RegSetValue(hKey,pszSubKey, dwType, pszData, cbData);
As with
RegSetValueEx
,
if the key named in pszSubkey
doesn’t exist,
it’s created. In an additional twist, if the key named by
pszSubkey
doesn’t exist,
RegSetValue
creates any keys necessary to
construct a legal path, then adds the default value to it. Note that
if all you want is to set the default value, you can do so using
RegSetValueEx
and passing NULL
for pszValueName
.
Example 8-2 illustrates how
RegSetValueEx
works; the example sets the
DiskSpaceThreshold
value to the percentage of disk
space you specify. This routine is used in a tool I wrote that
configures new servers with the desired default settings before
delivering them to customers or remote sites.
Example 8-2. SetDiskWarningThreshold
// This routine sets the DiskSpaceThreshold to the specified percentage. // You should check all the system's disk volumes to figure out a reasonable // percentage for the machine, then call this routine to set it. long SetDiskWarningThreshold(const int inThreshold) { char pszName[MAX_PATH] = "System\CurrentControlSet\Services\LanmanServer\Parameters"; HKEY hkResult = NULL; LONG nResult = ERROR_SUCCESS; // preflight our arguments if (inThreshold < 1 || inThreshold > 99) return ERROR_INVALID_PARAMETER; // open the key with write access so we can set the value nResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszName, 0L, KEY_WRITE, &hkResult); if (ERROR_SUCCESS == nResult) { nResult = RegSetValueEx(hkResult, "DiskSpaceThreshold", 0L, REG_DWORD, (unsigned char *)&inThreshold, sizeof(int)); if (ERROR_SUCCESS == nResult) nResult = RegCloseKey(hkResult); } return nResult; }
You may find it necessary to delete keys or values from within your home-brewed Registry utilities. Since many of the lesser-known features of Windows 2000 and NT discussed in Chapter 10, function based on the presence or absence of special trigger keys, turning these features on or off may require you to delete values, and there’s no way to do so with a .REG file. You must be careful with your newfound destructive powers, though; accidentally deleting the wrong key or value can make your system stop working altogether.
Before you delete a key or value, you must have the parent key opened
with adequate access. If you supply KEY_WRITE
as
the REGSAM
value when you open the key, you can
delete it. You can also request KEY_CREATE_SUB_KEY
or KEY_SET_VALUE
rights to gain delete access to
keys and values, respectively.
There’s one other thing worth mentioning here: when you delete
a key or a value, it’s not actually deleted. Instead, the
Registry subsystem marks the deleted items as deleted, but
doesn’t delete them until the next time Registry data is
flushed (either explicitly with
RegFlushKey
or automatically by the kernel’s lazy flusher). If you try to
read, write, or enumerate a key or value that’s been marked as
deleted, you get
ERROR_KEY_DELETED
as a return value. You can always call RegCloseKey
on a deleted key without getting an error, though.
You delete individual keys with the
RegDeleteKey
routine. If you specify a valid key and subkey, the key is
immediately marked for deletion, even if other processes currently
have the key open. This is different from the file metaphor used
elsewhere in the Registry; if you try to delete an open file, the
delete operation will fail, but not so with
RegDeleteKey
. At that point, attempts by other
processes to access data attached to the open key will fail.
LONG RegDeleteKey(hKey, pszSubKey);
HKEY |
hKey |
Key pointing to parent of target value; may be a root key or a subkey. |
LPCSTR |
pszSubkey |
Name of the subkey to be deleted; if |
You can’t delete a root key, and you can’t delete first-level subkeys of root keys. For example, you can’t remove HKLMSOFTWARE or HKCUSOFTWARE, but you can remove HKLMSOFTWAREMicrosoft (though I wouldn’t recommend it). In addition, you may not delete a key that has subkeys; if you try, you get an error. It’s okay to delete keys that have values; the values are deleted along with the key.
Under Windows 95 and 98, RegDeleteKey
deletes keys
that have subkeys. If your code depends on the standard Windows 2000
behavior of failing when a targeted key has subkeys, it works fine
under Win9x, but it deletes the subkeys without warning you! If you
want to use routines with more explicit semantics, consider using
SHDeleteKey
or
SHDeleteEmptyKey
.
Deleting values is wonderfully straightforward (as long as you have
KEY_WRITE
or KEY_SET_VALUE
access on the target key)!
RegDeleteValue
removes the specified value from the key you provide.
LONG RegDeleteValue(hKey, pszValueName);
HKEY |
hKey |
Key pointing to parent of target value. |
LPCSTR |
pszValueName |
Name of the value to be deleted. |
If pszValueName
is NULL
or
contains an empty string, RegDeleteValue
deletes
the default value (you know, the one that appears as <Default>
or No Name in Registry editors). Otherwise,
pszValueName
must contain the correct name of an
existing value.
Under Windows 2000, every object in the entire system has security information attached to it. Registry keys are just objects, so they too can have ACLs that control who can read, write, or delete the key and its values. Ordinarily, you don’t need to control these ACLs; when you do, RegEdt32 is probably the best tool for doing so. If you find it necessary or desirable to get a key’s security data programmatically, though, you certainly can.
ACLs come in two types: system ACLs, or
SACLs, are owned by (and can only be changed by) the system; while
discretionary ACLs (DACLs for short)
are controlled by the owner of the object. As you might expect from
security information, not just anyone can read either type of ACL. To
read the DACL, the requesting process must have
READ_CONTROL
access on the key. To get this
access, the requester must either own the key itself, or the DACL
must grant READ_CONTROL
to the account under which
the requester is running.
System ACLs are trickier. They can be read only by applications that
have been granted the ACCESS_SYSTEM_SECURITY
permission. In turn, the only way to get
ACCESS_SYSTEM_SECURITY
is for the calling process
to ask for the SE_SECURITY_NAME
privilege, open
the key with a REGSAM
value of
ACCESS_SYSTEM_SECURITY
, then turn off
SE_SECURITY_NAME
again.
To actually retrieve a key’s security data (assuming
you’ve fulfilled the access control requirements), you can use
RegGetKeySecurity
. Besides passing in the name
of the key whose information you want, you must also fill in the
SecurityInformation
field to indicate which data
you want. If you have permission,
pSecurityDescriptor
is filled with the ACL or
ownership data on return, and
lpcbSecurityDescriptor
contains the ACL size. ACLs
vary in size, since they may contain one or many entries.
LONG RegGetKeySecurity (hKey, SecurityInformation, pSecurityDescriptor, lpcbSecurityDescriptor);
HKEY |
hKey |
Open Registry key whose security information you want. |
SECURITY_INFORMATION |
SecurityInformation |
|
PSECURITY_DESCRIPTOR |
pSecurityDescriptor |
Pointer to record that receives the security descriptor specified by
|
LPDWORD |
lpcbSecurityDescriptor |
Points to a |
If the buffer pointed to by pSecurityDescriptor
is
too small, RegGetKey-Security
returns
ERROR_INSUFFICIENT_BUFFER
and the
lpcbSecurity-Descriptor
parameter contains the
number of bytes required for the requested security descriptor. This
makes it possible to efficiently allocate a buffer of the right size
by calling it twice, like this:
long retVal = 0, aclSize = 0; PSECURITY_DESCRIPTOR pWhat = NULL; retVal = RegGetKeySecurity(theKey, DACL_SECURITY_INFORMATION, pWhat, &aclSize); if (ERROR_INSUFFICIENT_BUFFER != retVal) throw(retVal); pWhat = malloc(aclSize); retVal = RegGetKeySecurity(theKey, DACL_SECURITY_INFORMATION, pWhat, &aclSize); if (ERROR_SUCCESS != retVal) throw(retVal);
If you’re not thoroughly familiar with how Windows 2000’s
security system works, stay away from
RegSetKeySecurity
until you have a good set of
Registry backups. Setting the wrong permissions on a key is much
easier to do programmatically than through any of the GUI editing
tools, so please be very careful.
Once you’ve gotten a security descriptor and modified
it,[43] you can write it back to the key that owns
it with
RegSetKeySecurity
.
LONG RegSetKeySecurity (hKey, SecurityInformation, pSecurityDescriptor);
To ensure that your new security data gets written, you should call
RegCloseKey
on the modified key after successfully
calling RegSetKeySecurity
. This is true even if
you’ve set security on one of the root keys; it won’t
actually be closed, but its cached security data is updated.
In Chapter 4, and
Chapter 5, you learned how to use
RegEdit and RegEdt32 to
edit Registry data on remote computers. Adding this same
functionality to your own programs is trivial: all you need do is
call
RegConnectRegistry
and use the HKEY it returns in any other calls you make to Registry
API functions. When you’re finished with the remote key, you
call RegCloseKey
on it as though it were a local
key. The API function declaration looks like the following.
LONG RegConnectRegistry(pszMachineName, hKey, phkResult);
HasPackage
(shown in Example 8-3) showcases
RegConnectRegistry
in action. You supply it with a
machine name and a subkey; it checks the Registry on the specified
machine to see whether it has a subkey of HKLMSOFTWARE by the name
you specify. The call to RegConnectRegistry
and
the corresponding RegCloseKey
on the key it
returns are the only changes needed to enable remote Registry
connections in this small program.
Example 8-3. HasPackage
void main(int argc, char **argv) { char pszName[MAX_PATH]; HKEY hkRemoteKey = NULL, hkResult = NULL; DWORD dwIdx = 0; LONG nResult = ERROR_SUCCESS; memset(pszName, 0x0, MAX_PATH); // preflight our arguments if (argc < 3) DoUsage(argv[0]); nResult = RegConnectRegistry(argv[1], HKEY_LOCAL_MACHINE, &hkRemoteKey); if (ERROR_SUCCESS == nResult) { sprintf(pszName, "SOFTWARE\%s", argv[2]); nResult = RegOpenKeyEx(hkRemoteKey, pszName, 0L, KEY_READ, &hkResult); if (ERROR_SUCCESS == nResult) { fprintf(stdout, "%s has a key for %s. ", argv[1], argv[2]); } else { fprintf(stderr, "Error %d while opening SOFTWARE\%s on remote machine %s ", argv[2], argv[1]); } nResult = RegCloseKey(hkResult); nResult = RegCloseKey(hkRemoteKey); } else { fprintf(stderr, "Error %d while opening remote registry on %s ", nResult, argv[1]); } fflush(stdout); }
In Chapter 3, Chapter 4, and Chapter 5, you learned how to use the Registry editor functions that allow keys and values to be saved into hive files and later restored. You can do the same thing with your own code by using the routines discussed in this section.
The first step in moving keys in and out of hives around is to create
a hive; you can do this with
RegSaveKey
.
LONG RegSaveKey(hKey, pszFile, lpSecurityAttributes);
If the file you specify in pszFile
already exists,
RegSaveKey
will fail with the
ERROR_ALREADY_EXISTS
error code. This prevents you from accidentally overwriting another
hive file you previously saved. There’s another subtlety
involved with pszFile
: if you don’t specify
a full path, the file is created in the process’s current
directory if the key is from the local Registry, or
%systemroot%system32 for a key on a remote
machine.
The created file has the archive attribute set and whatever
permissions are specified by lpSecurityAttributes
.
Instead of creating a brand-new security descriptor, you may pass
NULL
to have whatever security context applies to
the process applied to the file.
Once you’ve saved keys into a hive
file, the next thing you’re likely to want to do is load them.
You can do so in two distinct ways: you can load a hive as a new key,
or you can replace the contents of an existing key with the
hive’s contents. Either approach requires the process that
loads the keys to have the SE_RESTORE_NAME
privilege.
RegLoadKey
supports the former: you tell it what file to load and what to name
the new subkey, and it creates the specified subkey and loads the
file into it. RegLoadKey
will fail if the file
doesn’t exist or if the named subkey does exist.
LONG RegLoadKey(hKey, pszSubKey, pszFile);
Calling RegCloseKey
on a key loaded with
RegLoadKey
doesn’t unload it; instead, you
must call RegUnloadKey
as described later.
If you want to overwrite an existing key that’s part of one of
the standard hives, you can instead call
RegRestoreKey
.
Like RegLoadKey
, it takes a parent key and the
name of a file to load. However, in this case the parent key’s
subkeys are replaced by the contents of the file. For example, if you
open HKLMSOFTWAREMicrosoftWindows and pass that to
RegRestoreKey
, the key with that name persists,
but all subkeys and values beneath it are deleted. After
RegRestoreKey
returns, the victim key contains
whatever values and subkeys were in the loaded file.
LONG RegRestoreKey(hKey, pszFile, dwFlags);
Once you’ve loaded a hive file with
RegLoadKey
, you can replace the loaded key with
another hive file. This is a good way to dynamically swap between
several hives’ worth of data. However, changes don’t take
effect until the machine is restarted.
LONG RegReplaceKey(hKey, pszSubKey, pszNewFile, pszOldFile);
RegLoadKey
allows you to load a stored hive file as a
new hive under HKLM or HKU. Once you’ve loaded a hive, it makes
sense to have a way to unload it when you’re done, and
RegUnloadKey
provides that functionality.
LONG RegUnLoadKey(hKey, pszSubKey);
You can unload only keys you load yourself, which prevents unloading
(accidentally or on purpose) an important key such as HKLMSOFTWARE.
The process that calls RegUnloadKey
must have the
special SE_RESTORE_NAME
privilege.
If you want to write a program that does
something when a particular Registry key or value changes, you can do
so by sitting in an infinite loop and periodically checking the item
of interest to see whether it changes. This is terribly inefficient,
though, so it’s good that there’s another way to do it.
The RegNotify-ChangeKeyValue
routine allows you to register your interest in a particular key or
value, then go do something else. Your code gets a notification you
when the Registry key (or its attributes) changes; it doesn’t,
however, tell you if the key is deleted.
LONG RegNotifyChangeKeyValue (hKey, bWatchSubtree, dwNotifyFilter,hEvent fAsynchronous);
The Registry uses a “lazy flusher” to propagate changes from memory to disk. The overall goal is to minimize the number of disk I/O operations, since they tend to be relatively time-consuming. The lazy flusher achieves this goal by not immediately writing every change out to disk as it occurs. Instead, it aggregates changes and writes them when the system is mostly idle.
When you call
RegCloseKey
, whatever changes
you’ve made are thus not immediately copied to disk. There can
be an interlude of indeterminate length (Microsoft says “as
long as several seconds” without elaborating) before your data
actually hits the disk. For most applications, this is perfectly
acceptable. However, if for some reason you want to make sure your
changes get written to disk, you can use the
RegFlushKey
routine to immediately force a Registry update:
LONG RegFlushKey (hKey);
Calling this routine forces the lazy flusher to buckle down and flush
the specified key’s data to its hive file; it may also cause
other keys to be written as well. Flushing the cached data also
updates the .LOG
files that act as a backup copy
of the Registry. The Win32 SDK warns that this function is expensive,
so you shouldn’t call it often. RegFlushKey
returns ERROR_SUCCESS
when all goes well or a
standard Win32 error code for failed flush attempts.
[38] Well, all right: around 1990.
[39] MSDN and the Win32 SDK both document these differences, so I won’t go into them here.
[40] This notation gets its name from Charles Simonyi, the Microsoft developer who invented the scheme. As you might infer from his surname, he’s Hungarian. Despite the fact that it’s ugly and restrictive, it has caught on in Windows books, perhaps because Microsoft uses it exclusively in their header files and example code.
[41] Actually, these names would be replaced by SIDs, but you get the idea.
[42] Surprisingly,
there’s no RegEnumValueEx
. The original
function hasn’t changed since its introduction, so Microsoft
left it alone in Win32.
[43] I’m not about to talk about how you actually create or modify ACLs; that’s a book all by itself.
18.188.66.13