Reading and Modifying the Windows Registry Through Visual Basic

Roger Wynn

July 24, 1995

Click to open or copy the files in the VBREG sample application for this technical article.

In the past, developers of Microsoft® Visual Basic® applications had difficulty maintaining state information between executions of their program. This is because Microsoft Windows®-based programs (including Visual Basic) needed to write program state information to text files (.INI files), and often needed to parse the text in those files to obtain meaningful state information.

Now, with the advent of the Windows 95 and Windows NT™ 32-bit operating systems, programs can use a storage location called the Windows registry to maintain state information and register themselves with the system. Retrieval and modification of registry data is exposed through C language application programming interfaces (APIs) in the ADVAPI32.DLL file.

This is great for programmers using C and C++, but using these APIs requires additional work for application developers in other languages such as Visual Basic version 4.0.

As a Visual Basic 4.0 programmer, you must first declare registry function prototypes in a .BAS file that are compatible with the needed C Registry APIs in ADVAPI32.DLL. You also need to add compatible declarations for the structures and constants needed to call these APIs. The declarations of these prototypes, structures, and constants are all in the VBREG.BAS file included with VBREG, the sample application that accompanies this article. To use these Registry APIs with any 32-bit Visual Basic 4.0 project, simply include VBREG.BAS in the project.

Note   If you decided to add additional APIs, structures, or constants to VBREG.BAS, be very careful how you declare them (especially the function parameters). For the API calls from your Visual Basic code to work as expected, it is extremely important to declare the parameters to these functions correctly. More examples of these declarations are in the WIN32API.TXT file installed with Visual Basic 4.0, but you may need to modify the declarations to have them work correctly. You can also find additional useful registry constants in the WINNT.H file, and additional error constants in the WINERROR.H file installed with Microsoft Visual C++™.

With a file such as VBREG.BAS included in your project, you can now call the registry APIs from your Visual Basic. code. Remember that the parameters you pass must be valid and contain valid values. In addition, when STRING values are returned, space must be created for the string. These APIs, structures, and constants are illustrated in the VBREG sample application.

The Windows registry is organized under four main registry keys : HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, and HKEY_USERS. This article assumes that you are familiar with these keys, how subkeys can be used, and the types of values that can be contained in the registry. Subkeys can be represented in the form "HighestSubKey\NextLevelSubKey\LowerLevelSubKey\" (without the quotation marks) ,where “HighestSubKey” is the key directly below one of the four main registry keys.

Registry keys can be created, deleted, opened, and closed. As you might expect, a registry key must be created before it can be used, and opened before any values below it can be modified. During program execution, you should close any keys that are not currently being used, and you should only delete a key if you know that it will never be needed again.

Registry values can be created, queried, modified, and deleted. A value must have been created or modified (by using RegSetValueEx) before it can be queried. As with registry keys, you should only delete a value if you know that it will never be needed again.

In addition, registry keys and values can be enumerated to determine what entries are currently available. This can be useful if you are not sure what key or value you will be interested in at compile time.

It is interesting to note that the RegDeleteKey API will only delete a registry key if it has no subkeys. For this reason, VBREG does not allow the deletion of a key with subkeys. You could easily add this functionality by creating a procedure that enumerates through all the subkeys (and their subkeys) of a requested key and that deletes all the subkeys before deleting the requested key. An effective recursive procedure that checks for ERROR_ACCESS_DENIED from RegDeleteKey on each subkey should work well.

VBREG uses HKEY_LOCAL_MACHINE, as set in the following VBREG Form_Load() procedure:

hKey = HKEY_LOCAL_MACHINE

You can just as easily use one of the other four main registry keys by setting this variable to another one of these key values. Further, you could get this information from the user or a known registry location.

When using the Registry APIs, it is usually a good idea to check the return value from the function to see if it succeeded (it will return ERROR_SUCCESS if successful). If not, you can check the error value and determine how you want your program to behave, based on the type of error that occurred. It is also a good idea to trap errors in procedures that use the Registry APIs (such as CreateRegKey in the VBREG sample) to make your program behaves after an error occurs.

The values that VBREG creates and modifies are all of the REG_SZ (Unicode null terminated string) type. There are several other types of values that can be used in the registry, such as REG_BINARY, REG_LINK, and REG_DWORD. For example, to write a REG_DWORD instead of a REG_SZ type, the SetRegValue procedure’s RegSetValueEx call could be changed to:

lResult = RegSetValueEx(phkResult, sSetValue, 0, REG_DWORD, CInt(sValue), 4&)

This assumes that “sValue” contains a value that can be converted to an integer. You may need to make further modifications to the code to use these other type values.

Before you can use a registry key, it must be created. The RegCreateKeyEx API provides this functionality at run time. RegCreateKeyEx will create a new key if the key does not exist, and will simply open it if it already exists. Below is the RegCreateKeyEx call from VBREG’s CreateRegKey procedure:

CreateRegKey = (RegCreateKeyEx(hKey, SubKey & NewSubKey, 0, "", _
                REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, _
                SA, phkResult, Create) = ERROR_SUCCESS)

If RegCreateKeyEx returns ERROR_SUCCESS, you can check the Create parameter for either REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY to determine if a new key was created. In the call above, REG_OPTION_NON_VOLATILE creates a key that is persistent and KEY_ALL_ACCESS gives all users full access to the key. Other options can be specified, as well. These options are explained further in the Windows API Reference that comes with Microsoft Visual C++.

Once you have the needed registry keys, you can use RegOpenKey or RegKeyCreateEx to open them as necessary in the future. You may notice that VBREG uses RegKeyCreateEx where it seems RegOpenKey would do. I have found that some operations will return ERROR_ACCESS_DENIED if you do not use RegKeyCreateEx to open the key. This may create a little extra overhead; it will also actually create the key if it doesn’t already exist. If this is not what you want, you should try calling RegOpenKey first and see if it succeeds before making the call to RegCreateKeyEx.

Once a key has been opened, you can use RegSetValueEx to set a value and create it if necessary. If a value does not already exist and if you do not want to create it, you should call RegQueryValueEx first and test the return value for ERROR_SUCCESS before calling RegSetValueEx. Below is the RegSetValueEx call from VBREG’s SetRegValue procedure:

lResult = RegSetValueEx(phkResult, sSetValue, 0, REG_SZ, sValue, _
                        CLng(Len(sValue) + 1))

In the call above, REG_SZ is the type of value that is written. As mentioned before, you can write values as other types (such as REG_DWORD) if you pass the parameters correctly.

You can also query an existing value in the registry by using RegQueryValueEx. If the function is successful, it will return ERROR_SUCCESS. Below is a code segment from VBREG’s  GetRegValue procedure:

<<some code>>
' Create the buffer.
  szBuffer = Space(255)
  lBuffSize = Len(szBuffer)

' Open the key.
  RegOpenKeyEx hKey, lpszSubKey, 0, 1, phkResult

 ' Query the value.
lResult = RegQueryValueEx(phkResult, szKey, 0, 0, szBuffer, lBuffSize)
<<more code>>

The most important thing to note here is the creation of space for the buffer. With C language APIs that return a STRING value as an out parameter (such as parameter 5 of RegQueryValueEx), you must explicitly declare space for the parameter. Failing to do this can result in a number of undesirable consequences.

This article is intended to show you how the Windows Registry APIs can be used from a Visual Basic 4.0 application. If you would like more information on the Windows registry or on the Registry APIs, please refer to the Windows API Reference that comes with Microsoft Visual C++ and related articles in the Microsoft Development Library.