List object methods and properties

Using TypeLib Information Objects from tlbinf32.dll it is possible to list all members of a class.

`tlbinf32.dll` was part of *Visual Studio 6.0*, which was the current release in the years around 2000. Microsoft seems to not offer the DLL for download anymore (situation mid 2017), but you can download it from various sites on the internet. I found version *1.1.88.4, Build 8804, Copyright Matthew Curland 1996, Microsoft 1997-2000, size 148.480 Bytes* at https://www.dll4free.com/tlbinf32.dll.html, or other sites. To install the DLL in Win32, copy it to `%windir%\System32` and *as administrator* call `regsvr32.exe tlbinf32.dll` from that directory. To insttall the DLL in Win64, copy it to `%windir%\syswow64`, then *as administrator* register with `%windir%\syswow64\regsvr32.exe`, and finally run the vbscript with `%windir%\syswow64\cscript.exe` (or `wscript.exe`). Thanks [BuvinJ](/users/3220983/buvinj) for the [hint](/questions/14305750/list-object-methods-and-properties/44459670?noredirect=1#comment86169321_44459670)

The following script demonstrates the included function VariableInfo which will return a string with the type of the passed variable, and in case of an Object, all members with details, including type of Property, callable type (Sub or Function), and parameter names and return type in case of Function. The type name of the object in case of a COM object would be the name of the implemented Interface. Not sure if it works for multiple implemented interfaces, but AFAIK it's not possible to implement multiple interfaces in one class via COM anyway.

It does not support recursion in any way, because this would lead to infinity loops for some types.

This will give you virtually full working reflection in VBS. Great to explore APIs for example with the Microsoft Script Debugger.

' Reflection for VBScript via tlbinfo32.dll
'
' Patrick Strasser-Mikhail 2017-2021
' Ansgar Wiechers 2019
' https://stackoverflow.com/questions/14305750/list-object-methods-and-properties/44459670#44459670
'
' v1.1 2021-02-01: Show values of arrays and objects, but only one level


' Returns a String describing the passed object/variable on the first level,
' no recursion.
Function VariableInfo(obj)
    VariableInfo = VariableInfoToLevel(obj, 0, 1)
End Function

' Returns a String describing the passed object/variable on the first level,
' recurse down to level max_level(0=no recursion).
Function VariableInfoToLevel(obj, level, max_level)
    Const invokeKindPropertyGet = 0     ' simple data member
    Const invokeKindFunction = 1        ' method: Sub or Function
    Const invokeKindPropertyPut = 2     ' Docs: has a value setter; reality: more like is settable
    Const invokeKindPropertyPutRef = 4  ' Docs: has a reference setter; reality: more like is not settable

    If level > max_level Then
        VariableInfoToLevel = ""
        Exit Function
    End If

    Dim indent : indent = Space(4 * level)
    VariableInfoToLevel = indent

    If isEmpty(obj) Or _
       isNull(obj) _
    Then
        VariableInfoToLevel = VariableInfoToLevel & TypeNameFromVarType(VarType(obj))
    ElseIf Not IsObject(obj) Then
        If Not isArray(obj) Then
            VariableInfoToLevel = indent & TypeNameFromVarType(VarType(obj)) & ", Value: [" & obj & "]"
        Else
            VariableInfoToLevel = indent & TypeNameFromVarType(VarType(obj))
            Dim dimension
            ReDim sizes(0)
            Dim size

            On Error Resume Next
            Err.Clear

            For dimension = 0 To 10 ' deliberate limit to prevent infinite loop
                size = Ubound(obj, dimension + 1)
                If Err.Number <> 0 Then
                    ' report ther then Index out of Bounds
                    If Err.Number <> 9 Then 
                        WScript.Echo "Exception " & Err.Number & ": " & Err.Description & "; in " & Err.Source
                    End If
                    Exit For
                End If
                ReDim Preserve sizes(dimension)
                sizes(dimension) = size  
            Next
            On Error Goto 0

            VariableInfoToLevel = VariableInfoToLevel & "(" & Join(sizes, ",") & ")"
            Select Case dimension
              Case 1
                VariableInfoToLevel = VariableInfoToLevel & " {"  & vbCrlf
                Dim idx
                For idx = LBound(obj) To UBound(obj)
                    VariableInfoToLevel = VariableInfoToLevel & indent & _
                        "     " & idx & ":" & _
                        Trim(VariableInfoToLevel(obj(idx), level + 1, max_level)) & vbCrlf
                Next

                VariableInfoToLevel = VariableInfoToLevel & indent & "}" & vbCrlf
                
              Case 2
                VariableInfoToLevel = indent & "{" & vbCrlf

                Dim idx1, idx2
                For idx1 = LBound(obj, 1) To UBound(obj, 1)
                    For idx2 = LBound(obj, 2) To UBound(obj, 2)
                        VariableInfoToLevel = VariableInfoToLevel & indent & _
                            "     " & idx1 & "," & idx2 & ":" & _
                            Trim(VariableInfoToLevel(obj(idx1, idx2), level + 1, max_level)) & vbCrlf
                    Next
                Next

                VariableInfoToLevel = VariableInfoToLevel & indent & "    }" & vbCrlf
              
              Case Else
                ' 0 is empty anyway, more is too complicated to print, just leave it for now
            End Select
        End If
    ElseIf TypeName(obj) = "Nothing" Then
        VariableInfoToLevel = indent & "Nothing (The Invalid Object)"
    Else
        ' Object
        VariableInfoToLevel = indent & "Object " & TypeName(obj)
        '' Need to think about that... True for Err, but not for System.Dictionary
        '' Seems Err is very special, and we should compare explicitly with internal/predifined Objects (Err, WScript)
        'If varType(obj) <> vbObject Then
            ' hm, interresting...
        '   VariableInfoToLevel = VariableInfoToLevel & " with default property (no analysis possible)"
        '   Exit Function
        'End If
            
        Dim TLI
        Dim MemberInfo
        Dim TypeInfo
        Set TLI = CreateObject("TLI.TLIApplication")
        VariableInfoToLevel = indent & "Object " & TypeName(obj)

        On Error Resume Next
        Err.Clear
        Set TypeInfo = TLI.InterfaceInfoFromObject(obj)
        
        If Err.Number <> 0 Then
            
            VariableInfoToLevel = VariableInfoToLevel & "; Error " & Err.Number
            VariableInfoToLevel = VariableInfoToLevel & ": " & Err.Description
            Err.Clear
            On Error Goto 0
            Exit Function
        End If
        On Error Goto 0
        
        
        For Each MemberInfo In TypeInfo.Members
            Dim Desc
            Dim printNextLevel : printNextLevel = vbFalse
            Desc = ""
            ' based on .Net System.Runtime.IteropService.ComTypes
            '' FIXME: Call by Value/Reference and settable seems to be switched some
            '' InvokeKind seems to not encode value passing, rather settable/not settable
            '' Needs more work to decode byValue/byReference
            Select Case MemberInfo.InvokeKind
                Case InvokeKindFunction
                    If MemberInfo.ReturnType.VarType <> 24 Then
                        Desc = "  Function " & TypeNameFromVarType(MemberInfo.ReturnType.VarType)
                    Else
                        Desc = "  Sub"
                    End If

                    Desc = Desc & " " & MemberInfo.Name
                    Dim ParameterList
                    ParameterList = Array()
                    Dim Parameter
                    For Each Parameter In MemberInfo.Parameters
                        ReDim Preserve parameterList(UBound(ParameterList) + 1)
                        ParameterList(Ubound(parameterList)) = Parameter.Name
                    Next
                    Desc = Desc & "(" & Join(ParameterList, ", ") & ")"
                    'Set parameters = Nothing
                Case InvokeKindPropertyGet
                    Desc = "  Data Member " & MemberInfo.Name
                    printNextLevel = vbTrue
                Case InvokeKindPropertyPut
                    ' Seems to be 
                    Desc = "  Property " & MemberInfo.Name & " [set by val"
                    If IsGettable(obj, MemberInfo.Name) Then 
                        Desc = Desc & "/get"
                        printNextLevel = vbTrue
                    End If
                    Desc = Desc & "]"
                    'Stop
                Case InvokeKindPropertyPutRef
                    'Stop
                    Desc = "  Property " & MemberInfo.Name & " [set by ref"
                    If IsGettable(obj, MemberInfo.Name) Then 
                        Desc = Desc & "/get"
                        printNextLevel = vbTrue
                    End If
                    Desc = Desc & "]"
                    'Stop
                Case Else
                    Desc = "  Unknown member, InvokeKind " & MemberInfo.InvokeKind
            End Select
            VariableInfoToLevel = VariableInfoToLevel & vbNewLine & _
                                  indent & Desc
            If printNextLevel And level < max_level Then
                VariableInfoToLevel = VariableInfoToLevel & vbNewLine & _
                    VariableInfoToLevel(eval("obj." & MemberInfo.Name), level + 1, max_level)
            End If
        Next

        Set TypeInfo = Nothing
        Set TLI = Nothing
    End If
End Function

Function IsGettable(obj, memberName)
    Dim value
    On Error Resume Next
    Err.Clear
    value = eval("obj." & memberName)
    Stop
    If Err.Number <> 0 And _
       Err.Number <> 438 And _
       Err.Number <> 450 Then
        WScript.Echo Err.Number & ": " & Err.Description
    End If
    
    '438: Object doesn't support this property or method
    '450: Wrong number of arguments or invalid property assignment
    If Err.Number = 438 Or _
       Err.Number = 450 Then
        IsGettable = vbFalse
    Else
        IsGettable = vbTrue
    End If
    
End Function

Function IsSimpleType(obj)
    If (isEmpty(obj) Or isNull(obj)) And (Not IsObject(obj)) And (Not isArray(obj)) Then
        IsSimpleType = vbTrue
    Else
        IsSimpleType = vbFalse
    End If
End Function 

' Decode Type Number to something readable
Function TypeNameFromVarType(typeNr)
    Dim typeDetails
    set typeDetails = CreateObject("Scripting.Dictionary")

    typeDetails.add 0,  "vbEmpty (uninitialized variable)"
    typeDetails.add 1,  "vbNull (value unknown)"
    typeDetails.add 2,  "vbInteger" ' Short?
    typeDetails.add 3,  "vbLong" ' Integer?
    typeDetails.add 4,  "vbSingle"
    typeDetails.add 5,  "vbDouble"
    typeDetails.add 6,  "vbCurrency"
    typeDetails.add 7,  "vbDate"
    typeDetails.add 8,  "vbString"
    typeDetails.add 9,  "vbObject"
    typeDetails.add 10, "Exception"
    typeDetails.add 11, "vbBoolean"
    typeDetails.add 12, "vbVariant"
    typeDetails.add 13, "DataObject"
    typeDetails.add 14, "vbDecimal"
    typeDetails.add 17, "vbByte"
    typeDetails.add 18, "vbChar"
    typeDetails.add 19, "ULong"
    typeDetails.add 20, "Long" ' realy Long?
    typeDetails.add 24, "(void)"
    typeDetails.add 36, "UserDefinedType"

    If typeDetails.Exists(typeNr) Then
        TypeNameFromVarType = typeDetails(typeNr)
    ElseIf typeNr > 8192 Then
        TypeNameFromVarType = "vbArray{" & TypeNameFromVarType(typeNr - 8192) & "}"
    Else
        typeNameFromVarType = "Unknown Type " & typeNr
    End If
End Function

' Some nice example class to demonstrate all possible interfaces.
Class MyClass
    Dim Name_
    Dim Name2_
    Dim Name3_
    Dim Name4_
    Dim dict

    Private Sub Class_Initialize()
        Name_ = "foo"
        Name2_ = "bar"
        Name3_ = "baz"
        Name4_ = "spam"
        Set dict = CreateObject("Scripting.Dictionary")
    End Sub
    
    Private Sub Class_Terminate()
        Set dict = Nothing
    End Sub
        
    Public Property Get Name
        Name = Name_
    End Property

    Public Property Let Name(ByVal Value)
      Name_ = Value
    End Property

    Public Property Let Name2(ByRef Value)
      Set Name2_ = Value
    End Property

    Public Property Get Name3
      Name3 = Name3_
    End Property

    Public Property Set Name3(ByVal Value)
      Set Name3_ = Value
    End Property

    Public Property Get Name4
      Name4 = Name4_
    End Property

    Public Property Set Name4(ByRef Value)
      Set Name4_ = Value
    End Property

    Sub TestSub()
        WScript.Echo "Test"
    End Sub

    Sub TestFunc(message)
        WScript.Echo "Test: " & message
    End Sub

    Sub TestFunc2(ByRef message)
        WScript.Echo "Test: " & message
    End Sub

    Function Add(first, second)
        Add = first + second
    End Function

    Function Substract(ByVal first, ByRef second)
        Add = first - second
    End Function

End Class

Sub testVariableInfo()
    Dim variable
    ' vbEmpty
    Wscript.Echo VariableInfo(variable)

    variable = Null
    Wscript.Echo VariableInfo(variable)

    Set variable = Nothing
    Wscript.Echo VariableInfo(variable)

    Wscript.Echo VariableInfo(Int(23))
    Wscript.Echo VariableInfo(cLng(23))
    Wscript.Echo VariableInfo(2147483647)
    Wscript.Echo VariableInfo(5/4)
    Wscript.Echo VariableInfo(4 * Atn(1)) ' Simplest way to pi, not all inverse functions like arcsin are defined.
    Wscript.Echo VariableInfo(3.4E38)
    Wscript.Echo VariableInfo(CDbl(3.4E38))
    Wscript.Echo VariableInfo(cCur(20.123456))
    Wscript.Echo VariableInfo(now)
    Wscript.Echo VariableInfo("Some Text")
    Wscript.Echo VariableInfo(Err)
    
    Dim MyObject
    Set MyObject = new MyClass
    Wscript.Echo VariableInfo(MyObject)
    Set MyObject = Nothing

    Dim TestAEmpty()
    Wscript.Echo VariableInfo(TestAEmpty)

    ReDim TestA1(17)
    Wscript.Echo VariableInfo(TestA1)

    Dim TestA2(3, 7)
    Wscript.Echo VariableInfo(TestA2)
   
    Dim TestA3
    TestA3 = Array(4, 5, 6)
    Wscript.Echo VariableInfo(TestA3)

    Dim dict
    Set dict = CreateObject("Scripting.Dictionary")
    WScript.Echo VariableInfo(dict)
    Set dict = Nothing
End Sub

testVariableInfo

For for more information about the Typelib Interface, get the documentation help file from Microsoft KB artivle 224331

Matthew Curland offers for download at the website to his book Advanced Visual Basic 6 the nice program Type Library Editor (EditTLBEval.exe) as evaluation version, and the according Documentation

Especially in this context I really like the line If you're a Visual Basic developer who refuses to recognize the commonly accepted limitations of VB, this book is definitely for you. by Ted Pattison. Just replace VB by VBScript here.

VBWebProfi gave the hint for TLI, thanks for that. Working out the details and writing the code was several hours of work, though ;-)


VBScript itself does not support type introspection outside the TypeName and VarType functions, which will give you the type of an object, but won't give you access to its internal structure.

As other answers explained there is a DLL that would provide this feature, but it doesn't ship with Windows, and since it was part of an old version of Visual Studio there might not be a legal way to obtain it nowadays.


If you happen to be using HP UFT or QTP then follow these steps:

1) Install any version of MS Visual Studio onto your laptop. (Don't worry about licensing, you won't be running VS)

2) Reboot your computer.

3) Launch UFT or QTP, load a script and hit F11, (or pause at any piece of code that is near the object you want to inspect).

4) Add the object to the Watch window. It can be an Object Repository object or a programmatic description.

If the object exists, the object will now display two Plus (+) signs in the Watch window that can be expanded to show all available Methods and Properties, as well as child objects that can be expanded.


While that is partially true, its incomplete.... Google, GetObjectText_, Methods_, & Propeties_

The referenced methods will only work on objects collected while connected to the cimv2 namespace of a remote host via the WbemScripting.SWbemLocator object. If this object has the ability to work on localhost, it's unapparent to me.

Once you do this you can query any of the classes held therein [Win32_Services,Win32_Drives, etc] and interrogate the objects in the resultset using a For-Next loop on the object like below...

For Each oProp in oObject.Properties_
    'be careful here because some propeties may be an object or an array.
    'so test for that here using "typename" or "vartype"
    wScript.Echo oProp.Name & vbTab & oProp
Next

Or...

For Each oMethod in oObject.Methods_
    wScript.Echo oProp.Name
Next

Finally, ...

For Each oProp in oObject.Properties_
   'This will display all of an objects properties
   oProp.GetObjectText_
Next