Team LiB   Previous Section   Next Section

8.2 The Shell Utility API Routines

The shell utility API routines allow you to use a different set of API routines instead of the ones I've discussed up until now. While this might seem like a needless fragmentation of what should be a unified API, there's are two reasons for it. First, some of the tasks that the shell utility API can do don't have direct equivalents in the Win32 API. For example, the shell API has routines to copy a key and its contents, delete an entire key and all its subkeys, and so on. Second, Microsoft is clearly trying to position the utility API as the thing you use if you're writing small utility components- not all of which will necessarily run on Windows as we know it today. (For example, what about an embedded version of Windows running on a smart card?)

The shell API routine definitions are unnecessarily confusing because of their naming scheme (or lack of one.) Most of the Registry-related routines have names that start with SHReg, but some don't (e.g., SHDeleteValue or SHGetValue). Worse still, the file association routine names don't use the SH prefix at all.

8.2.1 Working with File Associations

One of the biggest innovations of the Macintosh when it was originally introduced was the idea that documents would somehow "know" what application they belonged to. The Mac OS implemented this by storing eight bytes of type and creator information with each file. The type bytes identified the file's datatype, and the creator bytes associated the file with a particular application. Windows 3.0 introduced the concept of file associations to the PC world, but the Windows version was based on binding a three-character DOS filename extension to an application. Later versions of Windows expanded this mapping so that object types (like, say, an Excel table) could be mapped to an executable; in addition, MIME types could be associated with file extensions too.

Managing these associations has long been painful for programmers. In particular, it's difficult to find what application or DLL the user wants called to create or modify some existing object type, even more so under Windows 2000. Recall that Windows 2000 stores class information in two places: HKCR and HKCU\Classes. In an effort to ease this pain, Microsoft created a Component Object Model (COM) interface called IQueryAssociations. By making the appropriate COM calls, you can get a pointer to the IQueryAssociations interface, call some of its routines, and get the desired information. However, not everyone is comfortable using COM, so Microsoft added a set of wrapper routines in the shell utility API. These handle all the messy COM calls while providing a simple C interface to their functionality.

8.2.1.1 Getting a file association key from the Registry

One common operation is to look up the file association for some piece of data you have, like a file extension or a CLSID. There are three ways to do so, depending on what data you have and what kind of data you want back.

If you want to get an HKEY that points to the key where the association is stored, you can do so with the AssocQueryKey routine.

HRESULT AssocQueryKey (afFlags, akKey, pszAssoc, pszExtra, HKEY *phkResult)

ASSOCF
afFlags

ASSOCF flags that control how you want the association retrieved. You can combine any number of flags, except that you can use one of the ASSOCF_INIT variants.

ASSOCKEY
akKey

ASSOCKEY structure that specifies what kind of key you want back: one to pass to the ShellExec( ) routine, etc.

LPCTSTR
pszAssoc

Query string that looks up the key (described later).

LPCTSTR
pszExtra

Optional additional information (e.g., a shell verb) that goes with pszAssoc; pass NULL if not used.

HKEY *
phkResult

On exit, pointer to an HKEY containing the found key.

In large part, the result you get back is determined by the value you pass in the pszAssoc parameter. You can pass in a file extension (.cpp), a CLSID (using its GUID, in the standard "{GUID}" format), a ProgID (e.g., "Excel.Worksheet.2"), or the name of an .exe file (but only if the ASSOCF_OPEN_BYEXENAME flag is set).

If you'd rather have a text string describing the association, use AssocQuery-String instead; it's similar to AssocQueryKey.

HRESULT AssocQueryString (afFlags, asStr, pszAssoc, pszExtra, pszOut, pcchOut)

ASSOCF
afFlags

ASSOCF flags that control how you want the association retrieved. You can combine any number of flags, except that you may use only one of the ASSOCF_INIT variants.

ASSOCSTR
akKey

ASSOCSTR structure that specifies what kind of string you want back, e.g., a command line, the "friendly" document name string, etc.

LPCTSTR
pszAssoc

Query string that looks up the key (described later).

LPCTSTR
pszExtra

Optional additional information (e.g., a shell verb) that goes with pszAssoc; pass NULL if not used.

LPCTSTR *
pszOut

On exit, holds the requested string.

LPDWORD
pcchOut

On entry, should hold the size of pszOut. On exit, holds the number of characters stored in pszOut. AssocQueryString truncates the string if the buffer is too small, unless you specify the ASSOCF_NOTRUNCATE flag, in which case this function returns E_POINTER as its result. In that case, pcchOut holds the buffer size required to hold the entire string.

The permissible values for pszOut are the same for this function as for AssocQueryKey.

The third way is a hybrid of the first two. When you call AssocQueryString-ByKey, you pass in the key where you want the routine to start looking, along with a mix of the parameters used for AssocQueryString and AssocQueryKey. Here's what it looks like.

HRESULT AssocQueryStringByKey (afFlags, asStr, hkAssoc, pszExtra, pszOut, pcchOut)

ASSOCF
afFlags

ASSOCF flags that control how you want the association retrieved. You can combine any number of flags, except that you can use only one of the ASSOCF_INIT variants.

ASSOCSTR
akKey

ASSOCSTR structure that specifies what kind of string you want back, e.g., a command line, the "friendly" document name string, etc.

HKEY
hkAssoc

HKEY from which you want AssocQueryStringByKey to start looking.

LPCTSTR
pszExtra

Optional additional information (e.g., a shell verb) that goes with pszAssoc; pass NULL if not used.

LPCTSTR *
pszOut

On exit, holds the requested string.

LPDWORD
pcchOut

On entry, should hold the size of pszOut. On exit, holds the number of characters stored in pszOut. AssocQueryString truncates the string if the buffer is too small, unless you specify the ASSOCF_NOTRUNCATE flag, in which case this function returns E_POINTER as its result. In that case, pcchOut holds the buffer size required to hold the entire string.

8.2.1.2 Getting a pointer to the IQueryAssociations interface

If you need to call one of the routines in the IQueryAssociations class that doesn't have a wrapper, you can do so by getting a pointer to an object that contains that interface and using the pointer to call the routine you want. While a discussion of writing this sort of COM code is outside the scope of this book, the shell utility API routine provides an easy way to get a pointer to the interface so you can use it: AssocCreate. Be aware that this routine doesn't actually create anything you can use directly; all it does is return a pointer to a COM interface, creating an object to handle that interface if necessary.

HRESULT AssocCreate (clsID, refIID, pInterface)

CLSID
clsID

CLSID of the object that exposes the interface you want. This must always be set to the constant value CLSID_QueryAssociations.

REFIID
refIID

Reference ID of the interface you want to use. This must always be set to the constant value IID_IQueryAssociations.

LPVOID *
pInterface

Pointer to a pointer that, on exit, points to the interface.

8.2.2 Copying and Deleting Keys and Values

The original Registry API had a few weak areas. One was the annoying lack of any functions for copying keys and values from one place to another, as you might do when establishing initial settings for a new installation. The API routines for saving and restoring keys provided a somewhat clunky way to do this, but the shell utility API provides a cleaner way. SHCopyKey (available with Version 5.0 or later of SHLWAPI.DLL) recursively copies all the subkeys and values beneath the source key you specify to the destination key.

LONG SHCopyKey (hkSrcKey, szSrcSubkey, hkDestKey, fReserved)

HKEY
hkSrcKey

Source key whose subkeys you want to copy; must be open.

LPCTSTR
szSrcSubkey

Specific subkey of hkSrcKey you want to copy, not including initial backslashes.

HKEY
hkDestKey

Destination key under which you want the tree specified by szSrcSubkey to be copied; this key must already be open.

DWORD
fReserved

Reserved; must always be NULL.

Deleting keys in Win32 code has always been a little tricky. The semantic behavior of RegDeleteKey varies according to which operating system it runs on. In the Windows 9x family, it deletes the key whether or not it's empty, while under Windows 2000/NT, the function fails if the target key has any subkeys. In an effort to keep from surprising people with unexpected behavior, Microsoft added two separate routines to clearly distinguish two different operations: removing a key that you know (or suspect) to be empty and removing a key when you don't care if it's empty or not.

SHDeleteEmptyKey is what you use in the first case. It fails if the specified target key has any subkeys, although (like RegDeleteKey) it happily removes any values that are attached to the otherwise-empty target key.

HKEY SHDeleteEmptyKey (hkKey, pszTargetSubkey);

HKEY
hkKey

Handle to a root key or any currently open key (as long as it's an ancestor to the target).

LPCTSTR
pszTargetSubkey

Name of the key to remove, relative to hkKey.

If you really want to remove the key and all its subkeys, you should use SHDeleteKey instead. It recursively removes the target and all its subkeys, along with all values attached to the target or any of its descendants.

HKEY SHDeleteKey (hkKey, pszTargetSubkey);

HKEY
hkKey

Handle to a root key or any currently open key (as long as it's an ancestor to the target).

LPCTSTR
pszTargetSubkey

Path of the key to remove.

There's also a new routine intended for removing individual values: SHDeleteValue accepts the name of a subkey and value, then removes the value from that key. Unlike RegDeleteValue, which requires you to pass in an HKEY that points to the target value's parent key, SHDeleteValue accepts any open key as long as it's a parent of the target, plus the path from that key to the target's actual home.

HKEY SHDeleteValue (hkKey, pszParentSubkey, pszTargetValue);

HKEY
hkKey

Handle to a root key or any currently open key (as long as it's an ancestor to the target).

LPCTSTR
pszParentSubkey

Path to parent subkey that owns the value to remove.

LPCTSTR
pszTargetValue

Value name to remove.

8.2.3 Getting Key and Value Information

When you're ready to read and write individual keys and values, you'll probably find it helpful to get some fundamental information about the keys and values themselves.

8.2.3.1 Querying keys and values

RegQueryInfoKey gives you a wealth of information about a key--maybe too much for some uses. Most of the time, you need to know three things about a key: how many subkeys it has, how many values it has, and how much space to allocate when fetching things from it. SHQueryInfoKey provides most of this data.

LONG SHQueryInfoKey(hKey, pdwSubKeys, pdwMaxSubKeyLen, pdwValueCount,
          pdwMaxValueNameLen);

HKEY
hKey

Handle to any key or root key opened with KEY_READ or KEY_QUERY_VALUE access.

LPDWORD
pdwSubKeys

Pointer to DWORD that receives the number of subkeys under hKey.

LPDWORD
pdwMaxSubKeyLen

Pointer to DWORD that receives length of longest subkey name under hKey.

LPDWORD
pdwValueCount

Pointer to DWORD that receives total number of values under hKey.

LPDWORD
pdwMaxValueNameLen

Pointer to DWORD that receives length of longest value name under hKey.

For example, you can use it to find how many subkeys were beneath a particular key so you can efficiently enumerate them. One thing you can't do with it, though, is find out the length of the longest value under a key, as you can with RegQueryInfoKey. That means that if you want to preallocate a buffer to the size of the largest value under a key, you can't do it efficiently unless you use RegQueryInfoKey.

SHQueryValueEx is identical to RegQueryValueEx, down to the inclusion of the "reserved" parameter. That being so, I'm not going to cover it again here, but it exists if you want to use it.

8.2.3.2 Getting and setting values

Let's start with something familiar: SHQueryValueEx is a dead ringer for RegQueryValueEx. It takes the same parameters and has the same restrictions and conditions, so it's pretty much a drop-in replacement.

LONG SHQueryValueEx(hKey, pszValueName, pdwReserved, pdwType, pvData, pcbData);

HKEY
hKey

Handle to any key or root key opened with KEY_READ or KEY_QUERY_VALUE access.

LPTSTR
pszValueName

Name of the value to query; if NULL or empty, queries default value.

LPDWORD
pdwReserved

Unused; must be NULL .

LPDWORD
pdwType

On return, holds the datatype of the value (REG_DWORD, REG_SZ, etc.). If you pass in NULL, no type data is returned.

LPBYTE
pvData

Points to the buffer that holds the value's contents on return. If you pass in NULL, no value data is returned, but the pcbData parameter holds the length of the contents.

LPDWORD
pcbData

On input, points to the buffer that specifies the size of lpData buffer. On return, holds amount of data copied into lpData. You may pass in NULL if pvData is NULL also.

SHGetValue is a new routine. If you think of the fairly useless (and now deprecated) RegGetValue call, don't: SHGetValue is much more interesting, since it essentially duplicates RegQueryValueEx. Instead of passing the bogus "reserved" parameter, SHGetValue expects you to pass in a path that points to the key that owns the value you're trying to query.

LONG SHGetValue(hKey, pszSubKeyName, pszValueName, pdwType, pbData, pcbData);

HKEY
hKey

Handle to any key or root key opened with KEY_READ or KEY_QUERY_VALUE access.

LPCTSTR
pszSubKeyName

Path to subkey whose value you're querying.

LPDWORD
pszValueName

Name of the value to query; if NULL or empty, queries default value.

LPDWORD
pdwType

On return, holds the datatype of the value (REG_DWORD, REG_SZ, etc.). If you pass in NULL, no type data is returned.

LPBYTE
pbData

Points to the buffer that holds the value's contents on return. If you pass in NULL, no value data is returned, but the pcbData parameter holds the length of the contents.

LPDWORD
pcbData

On input, points to the buffer that specifies the size of pbData buffer. On return, holds amount of data copied into pbData. You may pass in NULL if pbData is NULL also.

When you want to set a value, you can use RegSetValueEx or its very close relative, SHSetValue. As with many of the other SH* routines and their vanilla Win32 counterparts, the primary difference between these two is that SHSetValue lets you specify the full path to the target key, instead of requiring you to open the target key and pass in a handle to it.

LONG SHSetValue(hKey, pszSubKeyName, pszValueName, pdwType, pbData, pcbData);

HKEY
hKey

Handle to any key or root key opened with KEY_READ or KEY_QUERY_VALUE access.

LPCTSTR
pszSubKeyName

Path to subkey whose value you're setting.

LPDWORD
pszValueName

Name of the value to set; cannot be NULL.

LPDWORD
pdwType

Contains the datatype of the value to be stored.

LPBYTE
pbData

Points to the buffer that holds the value's contents; may not be NULL.

LPDWORD
pcbData

Points to length of pbData.

8.2.4 Enumerating Keys and Values

The Registry API already contains a number of routines that enumerate keys and values. Guess what? The shell utility API contains more of them, including several normally used with user-specific values. In particular, there are two routines that duplicate existing functionality in the Registry API: SHEnumKeyEx and SHEnumValue. Why? Mostly because Microsoft wanted to provide a more straightforward set of routines without breaking applications that depended on the original API. Let's look at SHEnumKeyEx first.

LONG SHEnumKeyEx (hKey, dwIndex, pszName, pcchName)

HKEY
hKey

Handle to currently open key or root key.

DWORD
dwIndex

Index of the key to retrieve. For the first call, pass in zero; subsequent calls can use other index values.

LPCTSTR
pszName

Output buffer; on exit, contains full name of current key.

LPDWORD
dwFlags

On entry, points to DWORD containing size of pszName. On exit, contains the actual number of characters copied to pszName.

Like RegEnumKeyEx, SHEnumKeyEx allows you to iterate through every key under the specified subkey. Unlike its big brother, SHEnumKeyEx doesn't allow you to get the key's modification time. You can still use RegQueryInfoKey to determine in advance what indices to use, or you can start with zero and work your way up.

SHEnumValue is a somewhat redundant beast. It looks like RegEnumValue, except that SHEnumValuedoesn't have the useless "reserved" parameter. Other than that, the two are functionally identical.

LONG SHEnumValue (hKey, dwIndex, pszValueName, pcchValueName, pdwType, pvData,
pcbData)

HKEY
hKey

Handle to currently open key or root key.

DWORD
dwIndex

Index of the key to retrieve. For the first call, pass in zero; subsequent calls can use other index values.

LPTSTR
pszValueName

Output buffer; on exit, contains full name of enumerated value.

LPDWORD
pcchValueName

Pointer to a DWORD that contains size of pszValueName on entry and number of characters copied to pszValueName on exit.

LPDWORD
pdwType

Pointer to DWORD that receives datatype of the enumerated value, e.g., REG_SZ, REG_DWORD, etc.

LPVOID
pvData

Pointer to buffer that receives value contents. If you don't care, pass NULL; otherwise, size the buffer appropriately and put the size in pcbData.

LPDWORD
pcbData

Size of pvData buffer.

8.2.5 Working with User-Specific Keys

If you decide to use the shell API routines that give you access to user-specific keys (USKs), you'll find that they're familiar and different at the same time. Most of the things you have to do when using the standard Registry API are still required; in particular, you still have to open and close keys before you read or write them or their values, and the normal Win32 access controls still apply.

8.2.5.1 Creating and removing keys

Before you can do much of anything else with user-specific keys, you need to be able to create them. Of course, the counterpart of creating something is deleting it, so it's handy to know how to remove USKs as well.

You create a new USK with SHRegCreateUSKey, which also opens the key after creating it.

LONG SHRegCreateUSKey(pszPath, samDesired, hkRelUSKey, phkNewUSKey, dwFlags);

LPCTSTR
pszPath

Pointer to string containing name of subkey to create and open.

REGSAM
samDesired

Desired security access to the key when opening it.

HUSKEY
hkRelUSKey

Key to be used as base of relative path; if you specify a relative path in pszPath, it's interpreted relative to hkRelUSKey. If pszPath is absolute, set hkRelUSKey to NULL.

PHUSKEY
phkNewUSKey

Pointer to handle of the newly opened key.

DWORD
dwFlags

Flags that control the root key under which the new key is created and opened:

  • SHREGSET_HKCU creates the key under HKCU if it doesn't already exist under either HKCU or HKLM.

  • SHREGSET_FORCE_HKCU opens the key under HKCU if it exists and creates/opens it if not.

  • SHREGSET_HKLM creates the key under HKLM if it doesn't already exist.

  • SHREGSET_FORCE_HKLM opens the key under HKLM if it exists and creates/opens it if not.

  • SHREGSET_DEFAULT will create and open the key under both HKCU or HKLM, using whichever one it finds first.

The way this routine works requires some explanation. Since USKs can be created either under HKCU or HKLM, you have to use the dwFlags parameter to specify where you want the key created. Note that SHRegCreateUSKey creates the key and opens it. If the key already exists, you get to choose whether you want to open it in its existing location or force creation of a key with the same name under another root. When you request that a key be created under HKCU, though, you may find that it's actually created under the user's subkey of HKU.

Once you're done using a key (usually as part of your program's uninstallation code), it's polite to delete any USKs you've created. You do so with SHRegDeleteEmptyUSKey, which does just about what you'd expect.

LONG SHRegDeleteEmptyUSKey(hkUSKey, pszTargetPath, delRegFlags);

HUSKEY
hkUSKey

Currently open USK.

LPCTSTR
pszTargetPath

Full path to target key to delete; the target key must be empty.

SHREGDEL_FLAGS
delRegFlags

Flags that control which key you want to remove:

  • SHREGDEL_DEFAULT removes the key from HKCU if it exists or from HKLM if it's not under HKCU.

  • SHREGDEL_HKCU removes the key under HKCU if it exists.

  • SHREGDEL_HKLM removes the key if it exists under HKLM.

  • SHREGDEL_BOTH removes the key under HKCU and HKLM.

Note that SHRegDeleteEmptyUSKey refuses to delete a USK unless all its subkeys and values have been previously removed; this prevents you from accidentally removing settings you want to keep. In practice, that means you need to enumerate through any USKs you create and remove their contents before removing them at uninstall time.

8.2.5.2 Opening and closing keys

You have to open and close USKs just as you do regular keys. Since there's a new opaque type for USKs (HUSKEY), you can't intermix the shell utility routines and the ordinary Win32 API routines. To open a USK, use SHRegOpenUSKey.

LONG SHRegOpenUSKey(pszPath, samDesired, hkRelUSKey, phkNewUSKey, fIgnoreHKCU);

LPCTSTR
pszPath

Pointer to string containing name of subkey to open.

REGSAM
samDesired

Desired security access to the key once it's opened.

HUSKEY
hkRelUSKey

Key to be used as base of relative path; if you specify a relative path in pszPath, it's interpreted relative to hkRelUSKey. If pszPath is absolute, set hkRelUSKey to NULL.

PHUSKEY
phkNewUSKey

Pointer to handle of the newly opened key.

BOOL
fIgnoreHKCU

When TRUE, SHRegGetUSValue looks for the key in HKLM instead of HKCU.

Once you have the key returned in phkNewUSKey, you can pass it to any of the other USK-related functions covered in this section. When you're done, of course, you need to close the key by calling SHRegCloseUSKey.

LONG SHRegCloseUSKey(hkTarget);

HUSKEY
hkTarget

Open USK you want to close.

8.2.5.3 Getting key and value information

After you've opened a USK, you'll probably need to get information about the keys and values in it. There are a total of three routines that do so: SHRegQueryInfoUSKey, SHRegEnumUSKey, and SHRegEnumUSValue. These are similar to their non-USK counterparts. For example, SHRegQueryInfoUSKey takes the same parameters as SHQueryInfoKey and returns the same information: a count of how many values and subkeys the specified USK has, plus the length of the longest subkey and value names. There are a few differences, though.

When you want to enumerate the subkeys of a USK, SHRegEnumUSKey is the appropriate routine to use. Like all the other enumeration routines I've talked about in this chapter, SHRegEnumUSKey allows you to walk an entire USK and get the names of each of its subkeys. Unlike (say) SHRegEnumKeyEx, though, the USK version needs another parameter: a flag that indicates whether to return information about the USK under HKLM or HKCU.

LONG SHRegEnumUSKey(hUSKey, dwIndex, pszName, pcchName, enumRegFlags);

HUSKEY
hUSKey

Handle to the currently open USK.

DWORD
dwIndex

Zero-based index indicating which subkey you want information about.

LPCTSTR
pszName

Address of a buffer that receives the enumerated key's name; make sure the buffer is MAX_PATH characters long to ensure enough space.

LPDWORD
pcchName

On input, specifies size of pszName; on exit, specifies how many characters were copied to pszName.

SHREGENUM_FLAGS
enumRegFlags
  • SHREGENUM_DEFAULT enumerates the key under HKCU if it exists or from HKLM if it's not under HKCU.

  • SHREGENUM_HKCU enumerates the key under HKCU if it exists.

  • SHREGENUM_HKLM enumerates the key if it exists under HKLM.

  • SHREGENUM_BOTH isn't a legal value, even though it's defined in the header file.

SHRegEnumUSValue is just like SHEnumValue, with one exception: it adds an enumRegFlags parameter that accepts the same values as the one defined for SHRegEnumUSKey.

8.2.5.4 Reading values

When you want to read or write values under a USK, you'll again find that the process is quite similar to what you're accustomed to doing for ordinary Registry data. Once you have an open USK, you can get a specific value from it in two ways.

SHRegGetUSValue is the general-purpose routine for fetching USK values. You pass in the value's name and get back its contents, just like SHGetValue or RegQueryValueEx. However, since SHRegGetUSValue is for USKs, its argument list looks more like the other USK routines discussed thus far.

LONG SHRegGetUSValue(pszSubKey, pszValue, pdwType, pvData, pcbData, fIgnoreHKCU,
                                             pvDefaultData, dwDefaultDataSize);

LPCTSTR
pszSubKey

Path to subkey (relative to HKLM and/or HKCU) that contains the desired value.

LPCTSTR
pszValue

Name of the value to get.

LPDWORD
pdwType

On exit, contains value's datatype; pass NULL if you don't care what type it is.

LPVOID
pvData

Pointer to buffer for value's data. If you don't want the data returned, pass NULL.

LPDWORD
pchData

On entry, points to DWORD specifying size of buffer in pvData. On exit, contains actual count of bytes copied to pvData.

BOOL
fIgnoreHKCU

When TRUE, SHRegQueryUSValue returns a value from HKLM only, ignoring any values under the USK in HKCU.

LPVOID
pvDefaultData

Buffer that gets the default value's data; pass NULL if you don't want it.

DWORD
dwDefaultDataSize

Size of pvDefaultData buffer.

You may have noticed that there's no hUSKey parameter for this routine. That's because you don't have to open a USK to get a value with SHRegGetUSValue; it opens and closes the key for you. This is easy, but inefficient if you need to fetch several values in a row. In that case, you probably want to use SHRegQueryUSValue instead by opening the desired USK, calling it several times, and closing the key.

LONG SHRegQueryUSValue(hUSKey, pszValue, pdwType, pvData, pcbData, fIgnoreHKCU,
                                        pvDefaultData, dwDefaultDataSize);

HUSKEY
hUSKey

Handle to the currently open USK.

LPCTSTR
pszValue

Name of the value to be queried.

LPDWORD
pdwType

On exit, contains value's datatype; pass NULL if you don't care what type it is.

LPVOID
pvData

Pointer to buffer for value's data. If you don't want the data returned, pass NULL.

LPDWORD
pchData

On entry, points to DWORD specifying size of buffer in pvData. On exit, contains actual count of bytes copied to pvData.

BOOL
fIgnoreHKCU

When TRUE, SHRegQueryUSValue returns a value from HKLM only, ignoring any values under the USK in HKCU.

LPVOID
pvDefaultData

Buffer that gets the default value's data; pass NULL if you don't want it.

DWORD
dwDefaultDataSize

Size of pvDefaultData buffer.

There's also a special-purpose routine that gets a single value of type BOOL from a USK. SHRegGetBoolUSValue fetches a single Boolean value, using the same method as SHRegGetUSValue. It opens the specified key, retrieves the value, and returns it to you. This is an easy function to use, since its functionality is so limited, but it's still handy if you want to check the value of a Boolean flag in a USK within your program.

BOOL SHRegGetBoolUSValue (pszSubkey, pszValue, fIgnoreHKCU, fDefault)

LPCTSTR
pszSubkey

Path to subkey (relative to HKLM and/or HKCU) that contains the desired value.

LPCTSTR
pszValue

Name of the value you want to retrieve.

BOOL
fIgnoreHKCU

When TRUE, SHRegGetBoolUSValue looks only under HKLM.

BOOL
fDefault

Default value to be returned if the requested value doesn't exist.

For example, let's say you write an application that allows users to download the contents of an Outlook contacts folder to a GSM mobile phone. Most GSM phones can store numbers on a smart card in the phone or in the phone's internal RAM, so it would be nice if you let the user specify a default for where new numbers should go. At download time, it's fairly easy to check the setting before blasting data out to the phone.

// get all the contact info from Outlook

// see where the user wants the numbers stored
BOOL storeInPhone = SHRegGetBoolUSValue("Software\\RA\\PhoneBlaster",
				       "StoreNumbersInPhone", false, false);
if (storeInPhone)
	// store the numbers in the phone
else
	// store the numbers on the SIM card
8.2.5.5 Writing and deleting values

So far, I've talked only about reading values. Fortunately, there are routines you can use to store values in USKs too. In fact, there are two distinct ways to write a value. SHRegSetUSValue just sets the value without requiring you to open and close the USK you're using, while SHRegWriteUSValue expects to have an open USK passed to it. Like the functions you use to read values, the one you use depends on whether you're doing one operation (in which case SHRegSet-USValue is easier to use) or several (in which case SHRegWriteUSValue is more efficient).

LONG SHRegSetUSValue(pszSubKey, pszValue, dwType, pvData, cbData, dwFlags);

LPCTSTR
pszSubKey

Path to subkey (relative to HKLM and/or HKCU) where you want the value to go.

LPCTSTR
pszValue

Name of the value to write.

DWORD
dwType

Value's datatype; must be REG_SZ.

LPVOID
pvData

Pointer to buffer for value's string data.

DWORD
cbData

Length of the value string, not including the terminating NULL.

DWORD
dwFlags

Same flags specified for SHRegCreateUSKey:

  • SHREGSET_HKCU

  • SHREGSET_FORCE_HKCU

  • SHREGSET_HKLM

  • SHREGSET_FORCE_HKLM

SHRegWriteUSValue looks pretty much the same, except that it requires a handle to an open USK as once of its parameters:

LONG SHRegWriteUSValue(hkUSKey, pszValue, dwType, pvData, cbData, dwFlags);

HUSKEY
hkUSKey

Handle to currently open USK or one of the predefined root keys.

LPCTSTR
pszValue

Name of the value to write.

DWORD
dwType

Value's datatype; must be REG_SZ.

LPVOID
pvData

Pointer to buffer for value's string data.

DWORD
cbData

Length of the value string not including the terminating NULL.

DWORD
dwFlags

Same flags specified for SHRegCreateUSKey and SHRegSetUSValue:

  • SHREGSET_HKCU

  • SHREGSET_FORCE_HKCU

  • SHREGSET_HKLM

  • SHREGSET_FORCE_HKLM

Finally, you can delete a single value from a USK when you're done with it. SHRegDeleteUSValue does the trick (remember, you have to remove the values from a USK before you can remove it with SHRegDeleteEmptyUSKey). Removing a value is simple: you have to pass an open USK, the name of the value, and a flag word that indicates where you want to remove the value if found.

LONG SHRegDeleteUSValue(hkUSKey, pszValue, delRegFlags);

HUSKEY
hkUSKey

Handle to currently open USK.

LPCTSTR
pszValue

Name of the value to remove.

SHREGDEL_FLAGS
dwType

Flags that tell routine where to remove value if found. Flags that control which key you want to remove:

  • SHREGDEL_DEFAULT removes the key from HKCU if it exists or from HKLM if it's not under HKCU.

  • SHREGDEL_HKCU removes the key under HKCU if it exists.

  • SHREGDEL_HKLM removes the key if it exists under HKLM.

  • SHREGDEL_BOTH removes the key under HKCU and HKLM.

8.2.6 Leftovers

There are three routines that don't really belong anywhere else. Two of them allow you to read or change file paths, while the third duplicates a key handle. Let's look at it first; it's probably the simplest routine in the entire Registry API set.

HKEY SHRegDuplicateHKey (hkKey);

HKEY
hkKey

Key to duplicate.

SHRegDuplicateHKey does only one thing, but it does it well: it duplicates a current HKEY and makes a copy of it. If you're accustomed to the dup( ) function in Unix, this does exactly the same thing, so you can quickly duplicate a key handle (whether open or closed) and go on to do separate things to the two handles, independent of one another.

The path functions are also pretty straightforward. Windows 2000 really embodies Microsoft's philosophy that users should keep their data in their profiles, and that certain areas of the filesystem should be used only by applications and the OS. Accordingly, these functions allow you to pass in a path and have any symbolic names in it expanded to match actual folders on disk, or vice versa. The symbolic names that these functions support are shown in Table 8.8.

Table 8.8. Symbolic Names

Symbol Name

Expands to...

%USERPROFILE%

Current user's profile folder. Note that this variable can't be used by services that impersonate another account by manipulating the security context.

%ALLUSERSPROFILE%

The "All Users" profile folder.

%ProgramFiles%

The Program Files folder. This is normally not set under Windows NT.

%SystemRoot%

The system root directory where Windows 2000 is installed.

%SystemDrive%

The system volume's drive letter.

Although these routines work fine under Windows 95/98, the environment variables in Table 8.8 will probably be empty, since those operating systems don't support profiles.

The first of these routines, SHRegGetPath, takes the path to a Registry key that contains a file path. If that value is a REG_EXPAND_SZ, SHRegGetPath expands any symbolic names found in the path; if not, it returns the string without modifying it.

LONG SHRegGetPath (hKey, pszTargetSubkey, pszValue, pszExpandedPath, dwFlags)

HKEY
hKey

Handle to currently open key or root key.

LPCTSTR
pszTargetSubkey

String containing path to target subkey.

LPCTSTR
pszExpandedPath

Output buffer; on exit, contains fully expanded path. Set the size of this buffer to MAX_PATH to ensure there's enough room for the expansion.

DWORD
dwFlags

Reserved; always pass 0.

The counterpart of SHRegGetPath is SHRegSetPath, which takes a path string that contains one or more full path to the folders listed in the rightmost column of Table 8.8, then converts those paths to the symbols listed in the table.

LONG SHRegSetPath (hKey, pszTargetSubkey, pszValue, pszCompletePath, dwFFlags)

HKEY
hKey

Handle to currently open key or root key.

LPCTSTR
pszTargetSubkey

String containing path to target subkey. This subkey must exist or SHRegSetPath fails.

LPCTSTR
pszValue

String containing name of value to be created under pszTargetSubkey.

LPCTSTR
pszCompletePath

Output buffer; on exit, contains rewritten path. Set the size of this buffer to MAX_PATH to ensure there's enough room.

DWORD
dwFlags

Reserved; always pass 0.

The combination of these two functions allows you to store file paths in a reasonably flexible way, since if a user or administrator moves something (such as a user profile or the system drive), these API routines make that change transparent to your application.

    Team LiB   Previous Section   Next Section