RegOp Class for 64-bit VBA

Updating a classic VBA registry reading and writing class module for 64-bit compatibility.

RegOp Class for 64-bit VBA

For years I've used the RegOp class from Romke Soldaat to access the Windows registry when I wanted something more complicated than the GetSetting and SaveSetting methods.  

Unfortunately, like most VBA code posted to the internet during the glory days of VB6 development (late '90s to early '00s), the code is not compatible with 64-bit VBA.

Following Philipp Stiefel's excellent guidance, I updated the API declarations for 64-bit compatibility.

I've listed the updated API declarations below.  You'll need to copy and paste the listing from Romke's article into a new class module, then overwrite his API declarations with the code below for all that 64-bit goodness (be sure to read the important UPDATEs at the bottom of the article after the API declaration code):

#If VBA7 Then
    Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As LongPtr
        bInheritHandle As Long
    End Type
#Else
    Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As Long
        bInheritHandle As Long
    End Type
#End If
' RegCreateKeyEx creates the specified key. If the key
' already exists, the function opens it. The phkResult
' parameter receives the key handle.
#If VBA7 Then
    Private Declare PtrSafe Function RegCreateKeyEx _
                          Lib "advapi32.dll" Alias "RegCreateKeyExA" ( _
                              ByVal hKey As LongPtr, ByVal lpSubKey As String, _
                              ByVal Reserved As Long, ByVal lpClass As String, _
                              ByVal dwOptions As Long, ByVal samDesired As Long, _
                              lpSecurityAttributes As SECURITY_ATTRIBUTES, _
                              phkResult As LongPtr, lpdwDisposition As Long) As Long
#Else
    Private Declare Function RegCreateKeyEx _
                          Lib "advapi32.dll" Alias "RegCreateKeyExA" ( _
                              ByVal hKey As Long, ByVal lpSubKey As String, _
                              ByVal Reserved As Long, ByVal lpClass As String, _
                              ByVal dwOptions As Long, ByVal samDesired As Long, _
                              lpSecurityAttributes As SECURITY_ATTRIBUTES, _
                              phkResult As Long, lpdwDisposition As Long) As Long
#End If

'RegCloseKey releases a handle to the specified key.
'(Key handles should not be left open any longer than
'necessary.)
#If VBA7 Then
    Private Declare PtrSafe Function RegCloseKey Lib "advapi32.dll" ( _
                                    ByVal hKey As LongPtr) As Long
#Else
    Private Declare Function RegCloseKey Lib "advapi32.dll" ( _
                                     ByVal hCurKey As Long) As Long
#End If
                                     
                                     
' RegQueryInfoKey retrieves information about the specified
'key, such as the number of subkeys and values, the length
'of the longest value and key name, and the size of the
'longest data component among the key's values.
#If VBA7 Then
    Private Declare PtrSafe Function RegQueryInfoKey _
                          Lib "advapi32.dll" Alias "RegQueryInfoKeyA" ( _
                             ByVal hKey As LongPtr, ByVal lpClass As String, _
                             lpcbClass As Long, ByVal lpReserved As LongPtr, _
                             lpcSubKeys As Long, lpcbMaxSubKeyLen As Long, _
                             lpcbMaxClassLen As Long, lpcValues As Long, _
                             lpcbMaxValueNameLen As Long, lpcbMaxValueLen As Long, _
                             lpcbSecurityDescriptor As Long, _
                             lpftLastWriteTime As Long) As Long
#Else
    Private Declare Function RegQueryInfoKey _
                          Lib "advapi32.dll" Alias "RegQueryInfoKeyA" ( _
                              ByVal hCurKey As Long, ByVal lpClass As String, _
                              lpcbClass As Long, ByVal lpReserved As Long, _
                              lpcSubKeys As Long, lpcbMaxSubKeyLen As Long, _
                              lpcbMaxClassLen As Long, lpcValues As Long, _
                              lpcbMaxValueNameLen As Long, lpcbMaxValueLen As Long, _
                              lpcbSecurityDescriptor As Long, _
                              lpftLastWriteTime As Long) As Long
#End If

'RegEnumKeyEx enumerates subkeys of the specified open
'key. Retrieves the name (and its length) of each subkey.
#If VBA7 Then
    Private Declare PtrSafe Function RegEnumKeyEx Lib "advapi32.dll" _
                                     Alias "RegEnumKeyExA" (ByVal hKey As LongPtr, _
                                                            ByVal dwIndex As Long, ByVal lpName As String, _
                                                            lpcbName As Long, ByVal lpReserved As LongPtr, _
                                                            ByVal lpClass As String, lpcbClass As Long, _
                                                            lpftLastWriteTime As Long) As Long
#Else
    Private Declare Function RegEnumKeyEx Lib "advapi32.dll" _
                                      Alias "RegEnumKeyExA" (ByVal hCurKey As Long, _
                                                             ByVal dwIndex As Long, ByVal lpName As String, _
                                                             lpcbName As Long, ByVal lpReserved As Long, _
                                                             ByVal lpClass As String, lpcbClass As Long, _
                                                             lpftLastWriteTime As Long) As Long
#End If

'RegEnumValue enumerates the values for the specified open
'key. Retrieves the name (and its length) of each value,
'and the type, content and size of the data.
#If VBA7 Then
    Private Declare PtrSafe Function RegEnumValue Lib "advapi32.dll" _
                                      Alias "RegEnumValueA" (ByVal hKey As LongPtr, _
                                                             ByVal dwIndex As Long, ByVal lpValueName As String, _
                                                             lpcbValueName As Long, ByVal lpReserved As LongPtr, _
                                                             lpType As Long, lpData As Any, lpcbData As Long) As Long
#Else
    Private Declare Function RegEnumValue Lib "advapi32.dll" _
                                      Alias "RegEnumValueA" (ByVal hCurKey As Long, _
                                                             ByVal dwIndex As Long, ByVal lpValueName As String, _
                                                             lpcbValueName As Long, ByVal lpReserved As Long, _
                                                             lpType As Long, lpData As Any, lpcbData As Long) As Long
#End If

'RegQueryValueEx retrieves the type, content and data for
' a specified value name. Note that if you declare the
' lpData parameter as String, you must pass it By Value.
#If VBA7 Then
    Private Declare PtrSafe Function RegQueryValueEx _
                          Lib "advapi32.dll" Alias "RegQueryValueExA" ( _
                              ByVal hKey As LongPtr, ByVal lpValueName As String, _
                              ByVal lpReserved As LongPtr, lpType As Long, _
                              lpData As Any, lpcbData As Long) As Long
#Else
    Private Declare Function RegQueryValueEx _
                          Lib "advapi32.dll" Alias "RegQueryValueExA" ( _
                              ByVal hCurKey As Long, ByVal lpValueName As String, _
                              ByVal lpReserved As Long, lpType As Long, _
                              lpData As Any, lpcbData As Long) As Long
#End If

'RegSetValueEx sets the data and type of a specified
' value under a key.
' Note that if you declare the lpData parameter as String, you must pass it By Value.
#If VBA7 Then
    Private Declare PtrSafe Function RegSetValueEx Lib "advapi32.dll" _
                                       Alias "RegSetValueExA" (ByVal hKey As LongPtr, _
                                                               ByVal lpValueName As String, ByVal Reserved As Long, _
                                                               ByVal dwType As Long, lpData As Any, _
                                                               ByVal cbData As Long) As Long
#Else
    Private Declare Function RegSetValueEx Lib "advapi32.dll" _
                                       Alias "RegSetValueExA" (ByVal hCurKey As Long, _
                                                               ByVal lpValueName As String, ByVal Reserved As Long, _
                                                               ByVal dwType As Long, lpData As Any, _
                                                               ByVal cbData As Long) As Long
#End If

'RegDeleteValue removes a named value from specified key.
#If VBA7 Then
    Private Declare PtrSafe Function RegDeleteValue _
                          Lib "advapi32.dll" Alias "RegDeleteValueA" ( _
                              ByVal hKey As LongPtr, ByVal lpValueName As String) As Long
#Else
    Private Declare Function RegDeleteValue _
                          Lib "advapi32.dll" Alias "RegDeleteValueA" ( _
                              ByVal hCurKey As Long, ByVal lpValueName As String) As Long
#End If

'RegDeleteKey deletes a subkey. Under Win 95/98, also
'deletes all subkeys and values. Under Windows NT/2000,
'the subkey to be deleted must not have subkeys. The class
'attempts to use SHDeleteKey (see below) before using
'RegDeleteKey.
#If VBA7 Then
    Private Declare PtrSafe Function RegDeleteKey Lib "advapi32.dll" _
                                      Alias "RegDeleteKeyA" (ByVal hKey As LongPtr, _
                                                             ByVal lpSubKey As String) As Long
#Else
    Private Declare Function RegDeleteKey Lib "advapi32.dll" _
                                      Alias "RegDeleteKeyA" (ByVal hKey As Long, _
                                                             ByVal lpSubKey As String) As Long
#End If

'SHDeleteKey deletes a subkey and all its descendants.
'Under Windows NT 4.0, Internet Explorer 4.0 or later
'is required.
#If VBA7 Then ' Not Found in Win32API_PtrSafe.TXT
        Private Declare PtrSafe Function SHDeleteKey Lib "Shlwapi" _
                                     Alias "SHDeleteKeyA" (ByVal hKey As LongPtr, _
                                                           ByVal lpSubKey As String) As Long
#Else
    Private Declare Function SHDeleteKey Lib "Shlwapi" _
                                     Alias "SHDeleteKeyA" (ByVal hKey As Long, _
                                                           ByVal lpSubKey As String) As Long
#End If

#If VBA7 Then
    Private Declare PtrSafe Function LoadLibrary Lib "kernel32" _
                                     Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
#Else
    Private Declare Function LoadLibrary Lib "kernel32" _
                                     Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
#End If

#If VBA7 Then
    Private Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal hLibModule As LongPtr) As Long
#Else
    Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
#End If

#If VBA7 Then
    Private Declare PtrSafe Function ExpandEnvStrings Lib "kernel32" _
                                          Alias "ExpandEnvironmentStringsA" ( _
                                          ByVal lpSrc As String, ByVal lpDst As String, _
                                          ByVal nSize As Long) As Long
#Else
    Private Declare Function ExpandEnvStrings Lib "kernel32" _
                                          Alias "ExpandEnvironmentStringsA" ( _
                                          ByVal lpSrc As String, ByVal lpDst As String, _
                                          ByVal nSize As Long) As Long
#End If

#If VBA7 Then
    Private Declare PtrSafe Function GetVersionEx Lib "kernel32" _
                                      Alias "GetVersionExA" ( _
                                      lpVersionInformation As OSVERSIONINFO) As Long
#Else
    Private Declare Function GetVersionEx Lib "kernel32" _
                                      Alias "GetVersionExA" ( _
                                      lpVersionInformation As OSVERSIONINFO) As Long
#End If

Final Steps to Get the Code to Compile

Step 1. Replace hCurKey Declaration

UPDATE [2022-01-10]: You will also need to replace the following module-level declaration:

Private hCurKey

With this code:

#If VBA7 Then
    Private hCurKey As LongPtr
#Else
    Private hCurKey As Long
#End If

For more details, see here:

DefType Statements in VBA: The Dark Side of Backward Compatibility
Young VBA programmer, be not tempted by the dark side. DefType statements must be understood, but never used!

Step 2. Replace hLib Declaration

UPDATE [2022-01-12]: Additionally, you will need to replace the following code in Function ShlwapiInstalled:

Dim hLib As Variant

With this code:

#If VBA7 Then
    Dim hLib As LongPtr
#Else
    Dim hLib As Long
#End If

Special thanks to former Access MVP Tom Wickerath for reporting these two issues.

All original code samples by Mike Wolfe are licensed under CC BY 4.0