Enumerating Registry SubKeys Using WMI in VBA

I was trying to help a user out in a forum get a listing of all the SubKeys of a specific registry Key and thought I’d share my solution here.

Per the usual, there are a number of different ways to read the registry, but I found that using WMI is very simple.

To simplify things, I added the use of a ‘RegistryClass’ Enum so you don’t need to remember the various registry class constants.

Enough talk, here’s the code!

Enum RegistryClass
    HKEY_CLASSES_ROOT = &H80000000
    HKEY_CURRENT_USER = &H80000001
    HKEY_LOCAL_MACHINE = &H80000002
    HKEY_USERS = &H80000003
    HKEY_CURRENT_CONFIG = &H80000005
End Enum


'---------------------------------------------------------------------------------------
' Procedure : WMI_EnumerateKeySubKeys
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Enumerate all the subkeys in the specified registry key
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft WMI Scripting VX.X Library
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sRegClass : The Registry Class that the parent key is in
' sParentKey: The Key to enumerate the subkeys of
'
' Usage:
' ~~~~~~
' Call WMI_EnumerateKeySubKeys(HKEY_CURRENT_USER, "Control Panel\International")
' Call WMI_EnumerateKeySubKeys(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-02-03              Initial Public Release
'---------------------------------------------------------------------------------------
Sub WMI_EnumerateKeySubKeys(ByVal sRegClass As RegistryClass, ByVal sParentKey As String)
'https://docs.microsoft.com/en-us/previous-versions/windows/desktop/regprov/enumkey-method-in-class-stdregprov
    On Error GoTo Error_Handler
    '    #Const WMI_EarlyBind = False    'True => Early Binding / False => Late Binding
    #If WMI_EarlyBind = True Then
        Dim oWMI              As WbemScripting.SWbemObject
    #Else
        Dim oWMI              As Object
    #End If
    Dim aSubKeys              As Variant
    Dim sSubKey               As Variant
    Dim i                     As Long 'Sequential Counter

    Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
    oWMI.EnumKey sRegClass, sParentKey, aSubKeys    'Get all the subkeys within the sParentKey

    Debug.Print "No", "SubKey"      'Header
    Debug.Print String(80, "-")     'Header
    For Each sSubKey In aSubKeys    'Iterate through each subkey
        i = i + 1
        Debug.Print i, sSubKey
    Next

Error_Handler_Exit:
    On Error Resume Next
    If Not oWMI Is Nothing Then Set oWMI = Nothing
    Exit Sub

Error_Handler:
    MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: WMI_EnumerateKeySubKeys" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occured!"
    Resume Error_Handler_Exit
End Sub

You can then use the procedure by simply doing:

Call WMI_EnumerateKeySubKeys(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths")

which will return something along the lines of:

No            SubKey
--------------------------------------------------------------------------------
 1            7zFM.exe
 2            brave.exe
 3            cmmgr32.exe
 4            dfshim.dll
 5            excel.exe
 6            firefox.exe
 8            IEDIAG.EXE
 9            IEDIAGCMD.EXE
 10           IEXPLORE.EXE
...

Code Features

This code

  • can be used as both Early or Late Binding, the choice is yours and set by changing the value of the WMI_EarlyBind constant.
  • is bitness independent (works in both 32 and 64-bit installations)
  • is application independent (should work in any VBA applications – Access, Excel, Outlook, PowerPoint, Word, …)

A Few Resources on the Subject

2 responses on “Enumerating Registry SubKeys Using WMI in VBA

  1. Ian

    Hello Daniel,
    Thank you for sharing this code, it looks just what I need, however, I get a type mismatch error 13 when i run the following command to list COM ports:

    Call WMI_EnumerateKeySubKeys(HKEY_LOCAL_MACHINE, “HARDWARE\DEVICEMAP\SERIALCOMM”)

    Not sure if this would reproduce on your set up. But I’m running this on:

    Microsoft® Excel® for Microsoft 365 MSO (Version 2302 Build 16.0.16130.20754) 64-bit.

    Thanks

    1. Daniel Pineault Post author

      Without knowing exactly what you’re trying to do, I suspect that you don’t really want to use oWMI.EnumKey, but rather oWMI.EnumKey. You can learn more about it at: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/regprov/enumvalues-method-in-class-stdregprov.

      Sub WMI_EnumerateKeyValues(ByVal sRegClass As RegistryClass, ByVal sParentKey As String)
      On Error GoTo Error_Handler
      ' #Const WMI_EarlyBind = False 'True => Early Binding / False => Late Binding
      #If WMI_EarlyBind = True Then
      Dim oWMI As WbemScripting.SWbemObject
      #Else
      Dim oWMI As Object
      #End If
      Dim aNames As Variant
      Dim aTypes As Variant
      Dim i As Long 'Sequential Counter

      Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
      oWMI.EnumValues sRegClass, sParentKey, aNames, aTypes

      Debug.Print "No", "Value", , "Type" 'Header
      Debug.Print String(80, "-") 'Header
      For i = 0 To UBound(aNames)
      Debug.Print i + 1, aNames(i), aTypes(i)
      Next

      Error_Handler_Exit:
      On Error Resume Next
      If Not oWMI Is Nothing Then Set oWMI = Nothing
      Exit Sub

      Error_Handler:
      MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
      "Error Number: " & Err.Number & vbCrLf & _
      "Error Source: WMI_EnumerateKeyValues" & vbCrLf & _
      "Error Description: " & Err.Description & _
      Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
      , vbOKOnly + vbCritical, "An Error has Occured!"
      Resume Error_Handler_Exit
      End Sub