Creating a System Registry EditorTo illustrate how the Windows API Registry functions can be used to add, retrieve, update, and delete key values, the program that was started earlier in this chapter will be built upon so it can be used to edit INI files and the Registry. To keep things simple, only individual key values will be accessible. To begin modifying the sample program, first add the following declarations and constants to the INIEdit module (Listing 32.12): Listing 32.12. - Function declarations and constants to be added to the INIEdit module. |
Public Declare Function RegCloseKey Lib "advapi32" _ (ByVal hKey As Long) As Long Public Declare Function RegCreateKey Lib "advapi32" _ Alias "RegCreateKeyA" (ByVal hKey As Long, _ ByVal lpSubKey As String, phkResult As Long) As Long Public Declare Function RegDeleteValue Lib "advapi32" _ Alias "RegDeleteValueA" (ByVal hKey As Long, _ ByVal lpValueName As String) As Long Public Declare Function RegOpenKeyEx Lib "advapi32" _ Alias "RegOpenKeyExA" (ByVal hKey As Long, _ ByVal lpSubKey As String, ByVal ulOptions As Long, _ ByVal samDesired As Long, phkResult As Long) As Long Public Declare Function RegQueryValueEx Lib "advapi32" _ Alias "RegQueryValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal lpReserved As Long, _ lpType As Long, lpData As Any, lpcbData As Long) As Long Public Declare Function RegSetValueEx Lib "advapi32" _ Alias "RegSetValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal Reserved As Long, _ ByVal dwType As Long, lpData As Any, _ ByVal cbData As Long) As Long Public Const REG_NONE = 0 Public Const REG_SZ = 1 Public Const REG_EXPAND_SZ = 2 Public Const REG_BINARY = 3 Public Const REG_DWORD = 4 Public Const REG_DWORD_LITTLE_ENDIAN = 4 Public Const REG_DWORD_BIG_ENDIAN = 5 Public Const REG_LINK = 6 Public Const REG_MULTI_SZ = 7 Public Const REG_RESOURCE_LIST = 8
The first procedure that needs to be added
to the program is a function called GetKeyHandle (Listing 32.13). This function uses the
RegOpenKeyEx API function to open a subkey and get its handle, a unique number that can be
used to reference the subkey. Listing 32.13. - The GetKeyHandle function, which opens a subkey and returns its unique handle. |
Private Function GetSubkeyHandle(sSubkey As String) As Long Dim intSubkeyCount As Integer Dim intSubkeyLoop As Integer Dim intSubkeyPtr As Integer Dim lonHandle(1 To 32) As Long Dim lonLastHandle As Long Dim lonStatus As Long Dim strKeyPath As String Dim strSubkeys(1 To 32) As String Dim strTempKeys() As String ' Trim any spaces from left and right sides of the ' subkey path. strKeyPath = Trim$(sSubkey) ' Use VB's Split function to break the key's path ' into subkeys. strTempKeys = Split(strKeyPath, "\") ' Transfer the elements of the strTempKeys() ' array to strSubkeys(), but leave out any blank ' elements. For intSubkeyLoop = 0 To UBound(strTempKeys) If strTempKeys(intSubkeyLoop) <> "" Then intSubkeyCount = intSubkeyCount + 1 If intSubkeyCount <= 32 Then strSubkeys(intSubkeyCount) = strTempKeys(intSubkeyLoop) Else ' Error - too many subkeys in path. GetSubkeyHandle = -1 Exit Function End If End If Next intSubkeyLoop For intSubkeyPtr = 1 To intSubkeyCount - 1 ' For the first subkey, use the defined ' constants - this is the root key. If intSubkeyPtr = 1 Then Select Case UCase$(strSubkeys(1)) Case "HKEY_CLASSES_ROOT": lonLastHandle = &H80000000 Case "HKEY_CURRENT_USER": lonLastHandle = &H80000001 Case "HKEY_LOCAL_MACHINE" lonLastHandle = &H80000002 Case "HKEY_USERS": lonLastHandle = &H80000003 Case "HKEY_PERFORMANCE_DATA": lonLastHandle = &H80000004 Case "HKEY_CURRENT_CONFIG&quo;: lonLastHandle = &H80000005 Case Else: GetSubkeyHandle = -1 Exit Function End Select End If ' Open the next subkey. lonStatus = RegOpenKeyEx(lonLastHandle, _strSubkeys(intSubkeyPtr + 1), 0, &H3F, _ _ lonHandle(intSubkeyPtr)) ' A non-zero value returned by the ' RegOpenKey function indicates an error. If lonStatus Then ' Error in path. Close any previously ' opened subkeys. For intSubkeyLoop="2" To (intSubkeyPtr 1) lonStatus="RegCloseKey(lonHandle(intSubkeyLoop))" Next intSubkeyLoop GetSubkeyHandle="-1" Exit Function End If lonLastHandle="lonHandle(intSubkeyPtr)" Next intSubkeyPtr ' Return the value of the last subkey handle. GetSubkeyHandle="lonLastHandle" 'Close all subkeys but the last one. For intSubkeyLoop="1" To (intSubkeyCount 2) lonStatus="RegCloseKey(lonHandle(intSubkeyLoop))" Next intSubkeyLoop End Function
The object of this function is to return the
handle of the last subkey in the path, which is provided by the user via the
txtSectionName text box. Keep in mind that a path can be a long series of subkeys, such as
HKEY_CURRENT_USER\Software\Test App or something even longer. It is within the last subkey
that the individual keys and their values are contained. So it is that last subkey that
has to be opened. Unfortunately, that's not an easy task. To access the last subkey in the path, all of the preceding subkeys have to be opened, one at a time. So first you would open the HKEY_CURRENT_USER root key, then the Software subkey, then finally the Test App subkey. The first thing that needs to be done is to make sure there are no spaces surrounding the subkey path. Then the path is broken down into its various subkeys using VB's Split function, which was just introduced in VB6. This handy function saves a bit of coding! It uses the backslash ("\") delimiter of the path to break down the txtSectionName string into individual items, which are stored in the strTempKeys array. Because empty subkey names would be invalid, the strTempKeys array is transferred to the strSubkeys array and any empty items are weeded out. The strSubkeys array is only dimensioned for 32 elements, so a path with more subkeys than that will cause the function to fail. Now that the subkey path has been broken down, the function loops through the subkeys and opens each one. To see how that is done, take a look at the call to the RegOpenKeyEx API function: |
lonStatus = RegOpenKeyEx(lonLastHandle, _ strSubkeys(intSubkeyPtr + 1), 0, &H3F, _ _ lonHandle(intSubkeyPtr))
The first argument to the function is the
handle of the last subkey that was opened (lonLastHandle). Because this is not available
for the first subkey in the path, a Select...Case statement is used to assign a value to
lonLastHandle based on the first subkey, which must be one of the Registry's six root
keys. If it's not, the path is invalid and the GetSubkeyHandle function fails. The second argument to the RegOpenKeyEx function is the name of the subkey to be opened. That's easy, it's the next subkey in the strSubkeys array. The third argument is unused and is set to zero. The next argument is a Long value that specifies the operations that can be performed on the subkey. In this case, a value of &H3F is used to provide full access to the subkey. The final argument is assigned the handle of the subkey that is opened, which will be stored in the lonHandle array. If for some reason the RegOpenKeyEx function is not successful, then the GetSubkeyHandle function fails. But first, any currently open subkeys need to be closed with the RegCloseKey API function. You always want to make sure that you close any subkeys that you open. If all of the calls to the RegOpenKeyEx function are successful, then the GetSubkeyHandle function returns the handle of the last subkey that was opened. All of the handles that preceded it are closed before the function ends, leaving only the last subkey open. This is a pretty complicated routine, but if you break it down into its parts it may be easier to understand. Navigating along a subkey path is one of the more difficult aspects of Registry API programming. The next procedure to add (Listing 32.14) is a far more simple one. It closes a subkey when given its handle. Listing 32.14. - The CloseSubkey function, which closes an open subkey. |
Private Function CloseSubkey(lHandle As Long) As Long Dim lonStatus As Long ' Close the key with the handle specified by the ' lHandle argument. lonStatus = RegCloseKey(lHandle) ' Return zero if successful, -1 if not successful. CloseSubkey = 0 If lonStatus Then CloseSubkey = -1 End Function
Once again, the RegCloseKey API function is
used to close a subkey. The CloseSubkey function will be used in the next procedure to be
added, the RegRetrieve function (Listing 32.15). Listing 32.15. - The RegRetrieve function, which is used to retrieve a key value from the Registry. |
Private Function RegRetrieve() As String Dim lonSubkeyHandle As Long Dim lonStatus As Long Dim lonValueType As Long Dim lonValueSize As Long ' Use the GetSubkeyHandle function to determine the ' handle of the subkey given in txtSectionName. lonSubkeyHandle = GetSubkeyHandle(txtSectionName.Text) If lonSubkeyHandle = -1 Then ' Subkey could not be located. RegRetrieve = "Error - Subkey could not be located" Exit Function End If ' Use the RegQueryValueEx API function to retrieve ' the value of the key given in txtKeyName. lonValueType = REG_SZ lonValueSize = 256 lonStatus = RegQueryValueEx(lonSubkeyHandle, _ Trim$(txtKeyName.Text), 0, lonValueType, _ ByVal gstrKeyValue, lonValueSize) If lonStatus <> 0 Then ' Key could not be located. RegRetrieve = "Error - Key could not be located" Else RegRetrieve = "Key retrieved successfully" txtValue.Text = Left$(gstrKeyValue, lonValueSize) End If ' Close the open subkey with the CloseSubKey ' function. lonStatus = CloseSubkey(lonSubkeyHandle) End Function
If you remember back to the
cmdRetrieve_Click event that was added earlier in this chapter, the RegRetrieve function
is called when the Retrieve button is clicked and the Registry option button is selected.
This function does basically the same thing that the INIRetrieve function does, except
instead of retrieving a key from an INI file, it retrieves it from the Registry. This function assumes that the txtSectionName text box contains the name of the subkey path and the txtKeyName text box contains the name of the key value to be retrieved. To get to the key, the subkey path has to be followed and the last subkey has to be opened. This is accomplished with the GetSubkeyHandle function discussed earlier. After the subkey handle has been provided, the RegQueryValueEx API function is used to obtain the key value. If successful, the function places the value in the specified buffer, which in this case is gstrKeyValue. The lonValueType variable is set to REG_SZ, a constant that has a value of 1. This variable is passed to the RegQueryValueEx function to specify the type of value to be retrieved. The sample program will only deal with string values for simplicity's sake, but there are actually a number of different value types you can retrieve from a key, including binary data and long integers. The possible values are shown in Table 32.8. After the RegQueryValueEx function has been called, the status of the operation is determined and will be returned by the RegRetrieve function. If successful, the value in gstrKeyValue is displayed in the txtValue text box. Before the function ends, the subkey that was opened is closed because it is no longer needed. The next procedure to be added is the RegDelete function (Listing 32.16). It's used to delete a key and its value. Listing 32.16. - The RegDelete function, which deletes key values. |
Private Function RegDelete() As String Dim lonSubkeyHandle As Long Dim lonStatus As Long ' Use the GetSubkeyHandle function to determine the ' handle of the subkey given in txtSectionName. lonSubkeyHandle = GetSubkeyHandle(txtSectionName.Text) If lonSubkeyHandle = -1 Then ' Subkey could not be located. RegDelete = "Error - Subkey could not be located" Exit Function End If ' Use the RegDeleteKey API function to delete the ' key. lonStatus = RegDeleteValue(lonSubkeyHandle, _ txtKeyName.Text) ' Check status of the operation. If lonStatus <> 0 Then RegDelete = "Error - Key could not be deleted" Else RegDelete = "Key deleted sucessfully" End If ' Close the open subkey with the CloseSubKey ' function. lonStatus = CloseSubkey(lonSubkeyHandle) End Function
Once again, the GetSubkeyHandle function is
used to open the last subkey in the path given in the txtSectionName text box. Then the
RegDeleteValue API function is used to delete the key value. The subkey is then closed
because it is no longer needed. The next two procedures, RegUpdate and RegAdd, are almost identical (Listing 32.17). That's because the same API function (RegSetValueEx) is used to add or update a key value. Listing 32.17. - The RegUpdate and RegAdd functions, which both use the RegSetValueEx API function to add or update a key value. |
Private Function RegUpdate() As String Dim lonSubkeyHandle As Long Dim lonStatus As Long ' Use the GetSubkeyHandle function to determine the ' handle of the subkey given in txtSectionName. lonSubkeyHandle = GetSubkeyHandle(txtSectionName.Text) If lonSubkeyHandle = -1 Then ' Subkey could not be located. RegUpdate = "Error - Subkey could not be located" Exit Function End If ' Use the RegSetValueEx API function to update the ' key's value. lonStatus = RegSetValueEx(lonSubkeyHandle, _ txtKeyName.Text, 0, REG_SZ, ByVal txtValue.Text, _ Len(txtValue.Text)) ' Check status of the operation. If lonStatus <> 0 Then RegUpdate = "Error - Key could not be updated" Else RegUpdate = "Key updated sucessfully" End If ' Close the open subkey with the CloseSubKey ' function. lonStatus = CloseSubkey(lonSubkeyHandle) End Function Private Function RegAdd() As String Dim lonSubkeyHandle As Long Dim lonStatus As Long ' Use the GetSubkeyHandle function to determine the ' handle of the subkey given in txtSectionName. lonSubkeyHandle = GetSubkeyHandle(txtSectionName.Text) If lonSubkeyHandle = -1 Then ' Subkey could not be located. RegAdd = "Error - Subkey could not be located" Exit Function End If ' Use the RegSetValueEx API function to create a ' new key and set its value. lonStatus = RegSetValueEx(lonSubkeyHandle, _ txtKeyName.Text, 0, REG_SZ, ByVal txtValue.Text, _ Len(txtValue.Text)) ' Check status of the operation. If lonStatus <> 0 Then RegAdd = "Error - Key could not be added" Else RegAdd = "Key added sucessfully" End If ' Close the open subkey with the CloseSubKey ' function. lonStatus = CloseSubkey(lonSubkeyHandle) End Function
In both procedures, the GetSubkeyHandle
function is used to get the handle of the last subkey in the path. Then the RegSetValueEx
API function is called. The REG_SZ constant is used again here to indicate that the value
to be added or updated is of type string. The final bit of code to be added is for the optType option buttons, which are used to select whether an INI file or the System Registry is to be accessed. The optType_Click event (Listing 32.18) enables and disables command buttons and text boxes depending on which option is selected. Listing 32.18. - The optType_Click event, which is triggered when the user selects either the INI File or Registry option buttons. |
Private Sub optType_Click(Index As Integer) ' Enable/disable options depending on the state ' of the option buttons for INI files/Registry. If Index = 1 Then txtSectionName.Enabled = True txtKeyName.Enabled = True txtValue.Enabled = True cmdRefresh.Enabled = False mnuFile.Enabled = False lstFileContents.Enabled = False Else cmdRefresh.Enabled = True If dlgGetFilename.FileName = "*.ini" Then txtSectionName.Enabled = False txtKeyName.Enabled = False txtValue.Enabled = False cmdAdd.Enabled = False cmdRetrieve.Enabled = False cmdUpdate.Enabled = False cmdDelete.Enabled = False cmdRefresh.Enabled = False End If cmdRefresh.Enabled = True mnuFile.Enabled = True lstFileContents.Enabled = True End If End Sub
The only thing left to do is make a few
changes to the user interface. Change the caption of lblSectionName to Section Name/Subkey
Path because the text box it labels now serves a dual purpose. Also, change the optType(1)
option button's Enabled property to True so the Registry option can now be selected. Make sure you save the program before running it, because an errant API function can cause Visual Basic to crash. Also, heed this warning: Back up your System Registry before fooling around with modifying and deleting keys! One wrong move can have serious repercussions! To use the Registry editing functions of the program, first select the Registry option button. Then enter the path of a valid subkey in the Subkey Path text box, remembering that it must begin with one of the six root keys (see fig. 32.6). Enter a key name and a value in the appropriate text boxes. Then use the Add, Retrieve, Update, or Delete buttons to perform operations on the Registry. You can use the RegEdit program to build subkeys that you can experiment with. You can also use that program to visualize any changes that you make. The INI File/System Registry Editor dialog box. To manipulate keys, put the subkey path in the top text box and the key name in the middle text box. |
|
![]() |
![]() |
|||