WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

Reading and Writing Registry Keys with Visual Basic

by Ron Petrusha
06/15/2004

Since its introduction in Windows 95, the registry has more often than not intimidated developers. On contemporary systems, it is a large, multi-megabyte structure whose organization is confusing and whose contents, more often than not, are either under-documented or actually undocumented. Typically, developers are advised not to "muck with" the registry, lest the most dire of consequences (application crashes, system failure, or even a system that is unable to boot) result.

Yet although releases of Windows after Windows 95 have attempted to store critical information in other places (such as Active Directory and the IIS Metabase), the registry remains a critical, centralized database of system and application configuration information and settings. The ability to read from and write to the registry is an important component in the repertory of any developer.

Although the registry seems like an intimidating structure, in fact it is not. The registry is simply an inverse hierarchical tree-like structure, much like a file system. The major difference is that, while a file system consists of a single root per drive (the root directory or folder), the registry has either five or six roots, depending on the Windows version. These are:

  • HKEY_CLASSES_ROOT, which stores information about file associations and COM objects.
  • HKEY_CURRENT_USER, which stores the current user's configuration settings.
  • HKEY_LOCAL_MACHINE, which stores the local system's configuration settings.
  • HKEY_USERS, which stores settings for the default and other users.
  • HKEY_CURRENT_CONFIG, which stores the current system's hardware and software configuration.
  • HKEY_DYN_DATA, a set of volatile (nonpermanent) keys used to store plug-and-play hardware configuration information and performance statistics on Windows 95/98/ME systems.
  • HKEY_PERFORMANCE_DATA, a key used to support performance monitoring on Windows NT systems only.

There is also a difference in terminology. A file system consists of a root folder or root directory, which has one or more subfolders or subdirectories. Each subfolder or subdirectory in turn can have one or more subfolders or subdirectories, and so on. The registry, in contrast, consists of a number of root keys, each of which has multiple subkeys. Each subkey in turn can have one or more subkeys, and so on. In a file system, each folder can have files. In the registry, a key can have one or more values or value entries.

Figure 1 uses RegEdit, the registry editor, to illustrate the basic structure of the registry. One of the root keys, HKEY_CLASSES_ROOT, has numerous subkeys, one of which is .ADE. The HKEY_CLASSES_ROOT\.ADE key (notice the registry path, which describes the complete path to a registry key) in turn has a single subkey, Access.ADEFile.11. The HKEY_CLASSES_ROOT\.ADE\Access.ADEFile.11 key in turn has a single subkey, ShellNew. HKEY_CLASSES_ROOT\.ADE\Access.ADEFile.11\ShellNew has a single value, a default (or unnamed) value whose value has not been set.

Figure 1
Figure 1. The structure of the Windows registry.

Although registry keys often serve as containers for holding one or more values, they can also be important in their own right. Figure 1 is a case in point. The ShellNew registry key is in part a flag; its presence indicates that a file of this type can be created using the New option on an Explorer context menu. In many cases such as these, it's the presence or absence of a key, rather than the values the key contains, that is significant.

This article will examine methods available in Visual Basic to perform basic operations upon registry keys, first using the basic language features found in Visual Basic itself, and then using the Win32 API.

Visual Basic Language Support for the Registry

Visual Basic itself includes four intrinsic functions (GetSetting, GetAllSettings, SaveSetting, and DeleteSetting) that support registry access. The documentation for these functions is somewhat confusing, since the functions themselves were designed to support both initialization (.ini) file access under 16-bit Windows and registry access under 32-bit Windows.

The functions themselves, however, suffer from a number of limitations that make them unsuitable for general-purpose access to the registry. One of the most important of these is that the functions allow you to read and write only to keys that are subkeys of HKEY_CURRENT_USER\Software\VB and VBA Program Settings. This has three major implications:

  • The functions should be used only to store user-specific data. They should not be used to store application-specific or system-specific data, both of which should be stored in the subkeys of HKEY_LOCAL_MACHINE.

  • The functions cannot be used to access keys throughout the registry, but can only be used to access a very small portion of registry keys and values.

  • The registry is a dynamic structure. The exact contents of HKEY_CURRENT_USER, the per-branch of the registry, depends on the identity of the current user; there is a separate registry file for each user. This means that assuming that the relevant registry keys exist and attempting to access them just because the program has already been run once, for instance, can cause an application crash.

Given these limitations, as well as numerous others that we haven't discussed here, we recommend that you not use Visual Basic's intrinsic registry functions. Instead, registry access is best accomplished by using the Win32 API.

Registry Access through the Win32 API

By defining Win32 API functions using the Declare statement, you can access the entire registry and perform all operations that are supported by the Win32 API upon the registry. In order to do so, however, you need to define a number of constants in your code. First, each top-level registry key corresponds to an intrinsic constant, as follows:

Public Const HKEY_CLASSES_ROOT = &H80000000
Public Const HKEY_CURRENT_CONFIG = &H80000005
Public Const HKEY_CURRENT_USER = &H80000001
Public Const HKEY_LOCAL_MACHINE = &H80000002
Public Const HKEY_USERS = &H80000003

Second, each of the registry functions returns a numeric code indicating its success or failure. A successful function call returns the following:

Public Const ERROR_SUCCESS = 0&
Among the constants representing error codes are the following:
Public Const ERROR_FILE_NOT_FOUND = 2&         ' Registry path does not exist		
Public Const ERROR_ACCESS_DENIED = 5&          ' Requested permissions not available
Public Const ERROR_INVALID_HANDLE = 6&         ' Invalid handle or top-level key
Public Const ERROR_BAD_NETPATH = 53            ' Network path not found         
Public Const ERROR_INVALID_PARAMETER = 87      ' Bad parameter to a Win32 API function
Public Const ERROR_CALL_NOT_IMPLEMENTED = 120& ' Function valid only in WinNT/2000?XP	
Public Const ERROR_INSUFFICIENT_BUFFER = 122   ' Buffer too small to hold data 
Public Const ERROR_BAD_PATHNAME = 161          ' Registry path does not exist
Public Const ERROR_NO_MORE_ITEMS = 259&        ' Invalid enumerated value	
Public Const ERROR_BADDB = 1009                ' Corrupted registry	
Public Const ERROR_BADKEY = 1010               ' Invalid registry key
Public Const ERROR_CANTOPEN = 1011&            ' Cannot open registry key	
Public Const ERROR_CANTREAD = 1012&            ' Cannot read from registry key
Public Const ERROR_CANTWRITE = 1013&           ' Cannot write to registry key	
Public Const ERROR_REGISTRY_RECOVERED = 1014&  ' Recovery of part of registry successful
Public Const ERROR_REGISTRY_CORRUPT = 1015&    ' Corrupted registry
Public Const ERROR_REGISTRY_IO_FAILED = 1016&  ' Input/output operation failed
Public Const ERROR_NOT_REGISTRY_FILE = 1017&   ' Input file not in registry file format
Public Const ERROR_KEY_DELETED = 1018&         ' Key already deleted
Public Const ERROR_KEY_HAS_CHILDREN = 1020&    ' Key has subkeys & cannot be deleted

Third, some of the registry functions in the Win32 API have a parameter whose value is a security access mask indicating the rights that the current process must have in order for the function to execute successfully. The following are the possible values:

Public Const KEY_CREATE_LINK = &H20
Public Const KEY_CREATE_SUB_KEY = &H4
Public Const KEY_ENUMERATE_SUB_KEYS = &H8
Public Const KEY_NOTIFY = &H10
Public Const KEY_QUERY_VALUE = &H1
Public Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE 
                        Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) 
                        And (Not SYNCHRONIZE))
Public Const KEY_SET_VALUE = &H2
Public Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE 
                         Or KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE))
Public Const KEY_EXECUTE = ((KEY_READ) And (Not SYNCHRONIZE))
Public Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE 
                              Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY 
                              Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY 
                              Or KEY_CREATE_LINK) And (Not SYNCHRONIZE))

A bit of background about the operation of the registry functions in the Win32 is also important. Registry keys are system objects, and like any system object in the Win32 API, they are accessible through their handles. In other words, to work with a registry key, you must open it. In some form, the function that opens the key will return (usually in a parameter passed to the function by reference) a handle to the registry key, or hKey. With this background, and with these constants in place, you can proceed to access the registry API.

Pages: 1, 2, 3

Next Pagearrow