Vba – Using RegOpenKeyEx to enumerate through registry on 64bit office/64 bit Windows

registryvbawinapi

I've looked at various solutions for this to no avail. I posted this on another site, but no-one came up with the answer.

The main objective is to see if MySQL ODBC driver has been installed. I've been doing this by enumerating through the registry using RegOpenKeyEx. No problem using 32 bit Office on 64 Bit Windows.
But won't work on 64Bit Office on 64bit Windows.

The code below shows the many things I tried. When testing on 32 bit office, only the line with KEY_ALL_ACCESS works. Otherwise, none of the other lines work for either 32 or 64 bit.

And yes, on my 64Bit Office machine, the item ("MySQL ODBC 5.2 ANSI Driver") is in the registry located at: "HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI"

Any ideas?

   #If VBA7 Then

        Declare PtrSafe Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" ( _
          ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
          ByVal cbName As Long) As Long

        Declare PtrSafe Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" ( _
          ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
          lpcbName As Long, lpReserved As Long, ByVal lpClass As String, _
          lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long

        Declare PtrSafe Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" ( _
          ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, _
          lpcbValueName As Long, lpReserved As Long, lpType As Long, _
          lpData As Byte, lpcbData As Long) As Long

#else

    Declare Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" ( _
          ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
          ByVal cbName As Long) As Long

    Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" ( _
           ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
           lpcbName As Long, lpReserved As Long, ByVal lpClass As String, _
           lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long

    Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" ( _
           ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, _
           lpcbValueName As Long, lpReserved As Long, lpType As Long, _
           lpData As Byte, lpcbData As Long) As Long 

#End If


Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type    

Function CheckForMySQlDriverInstallTest() As Boolean

'*********BEGIN CODE HERE ********
Dim strKeyPath As String, key As String
Dim i As Long, lrc As Long
Dim hkey As Long, lRetval As Long

'Various key constants
Const KEY_ALL_ACCESS = &H3F
Const KEY_WOW64_64KEY As Long = &H100& '32 bit app to access 64 bit hive
Const KEY_WOW64_32KEY As Long = &H200& '64 bit app to access 32 bit hive
Const KEY_QUERY_VALUE = &H1


strKeyPath = "SOFTWARE\ODBC\ODBCINST.INI"

hkey = 0

'The line below works for 32bit office with the
' value of strKeyPath = "SOFTWARE\Wow6432Node\ODBC\ODBCINST.INI"

 lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_ALL_ACCESS, hkey)

'None of these work for 32 or 64 Office regardless of the strKeyPath used
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_WOW64_64KEY, hkey)
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_WOW64_32KEY, hkey)
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_QUERY_VALUE, hkey)

If (lRetval = 0) Then
    lrc = 0
    i = 0
    'Request all keys
    While lrc = 0
        lrc = EnumKey(hkey, i, key)
        Debug.Print key

        'If the version is found, set function to TRUE and exit
        If InStr(1, key, "MySQL ODBC 5.2 ANSI Driver") > 0 Then
            Exit Function
        End If

        If (lrc = 0) Then
            i = i + 1
        End If
    Wend
End If

If (hkey <> 0) Then
    RegCloseKey hkey
End If

End Function

Public Function EnumKey(ByVal hkey As Long, ByVal index As Long, ByRef key As String) As Long
Dim cch As Long
Dim lrc As Long
Dim ltype As Long
Dim lValue As Long
Dim szKeyName As String

cch = 260
szKeyName = String$(cch, 0)
lrc = RegEnumKey(hkey, index, szKeyName, cch)

If (lrc = 0) Then
    key = Left$(szKeyName, InStr(szKeyName, Chr$(0)) - 1)
End If

EnumKey = lrc
End Function

Best Answer

Your pointer sized integers are all the wrong size under 64 bit. You have used Long, which is a 32 bit data type, but you need to use LongPtr, which is the same size as a pointer. From the documentation:

LongPtr (Long integer on 32-bit systems, LongLong integer on 64-bit systems) variables are stored as signed 32-bit (4-byte) numbers ranging in value from -2,147,483,648 to 2,147,483,647 on 32-bit systems; and signed 64-bit (8-byte) numbers ranging in value from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 on 64-bit systems.

So, all the HKEY parameters, and all the pointers, need to be declared as LongPtr.

You really should not be using KEY_ALL_ACCESS. That won't succeed unless you are running elevated, and there's not need to elevate just to read out of HKLM. You need to combine the flags using bitwise or. You need to us

KEY_READ Or KEY_WOW64_64KEY

or

KEY_READ Or KEY_WOW64_32KEY