23

有没有办法列出 VBS 中创建的对象的可用方法?

例如:

Set IE = CreateObject("InternetExplorer.Application")

我想列出这个对象的可用属性,如:

IE.AddressBar
IE.Application
IE.Busy
...

或方法:

IE.ClientToWindow
IE.ExecWB
IE.GetProperty
...

如何在 VBS 中发现任意有效对象的可用属性?

4

6 回答 6

14

使用TypeLib Information Objectsfromtlbinf32.dll可以列出一个类的所有成员。

`tlbinf32.dll` 是 *Visual Studio 6.0* 的一部分,它是 2000 年左右的当前版本。Microsoft 似乎不再提供 DLL 供下载(2017 年中期的情况),但您可以从各个站点下载它互联网。我在 https://www.dll4free.com/tlbinf32.dll.html 或其他网站上找到了版本 *1.1.88.4、Build 8804、版权 Matthew Curland 1996、Microsoft 1997-2000、大小 148.480 字节*。 要在 Win32 中安装 DLL,请将其复制到 `%windir%\System32` 并 *作为管理员* 从该目录调用 `regsvr32.exe tlbinf32.dll`。 要在 Win64 中安装 DLL,请将其复制到 `%windir%\syswow64`,然后*以管理员身份* 注册到 `%windir%\syswow64\regsvr32.exe`,最后使用 `%windir%\syswow64\ 运行 vbscript cscript.exe`(或`wscript.exe`)。感谢 [BuvinJ](/users/3220983/buvinj) 的 [提示](/questions/14305750/list-object-methods-and-properties/44459670?noredirect=1#comment86169321_44459670)

以下脚本演示了包含的函数,该函数VariableInfo将返回具有传递变量类型的字符串,如果是 Object,则返回所有成员的详细信息,包括类型Property、可调用类型(SubFunction),以及参数名称和返回类型以防万一的功能。如果是对象,则对象的类型名称COM将是已实现接口的名称。不确定它是否适用于多个实现的接口,但是AFAIKCOM无论如何都不可能在一个类中实现多个接口。

它不以任何方式支持递归,因为这会导致某些类型的无限循环。

将使您在 VBS 中获得几乎完整的工作反思。非常适合探索 API,例如使用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

有关Typelib接口的更多信息,请从Microsoft KB 文章 224331获取文档帮助文件

Matthew Curland 在网站上为他的书Advanced Visual Basic 6提供了下载,作为评估版本的不错的程序类型库编辑器 (EditTLBEval.exe),以及相应的文档

尤其是在这种情况下,我真的很喜欢这句话如果你是一个拒绝承认 VB 普遍接受的局限性的 Visual Basic 开发人员,那么这本书绝对适合你。通过泰德帕蒂森。只需在这里用 VBScript 替换 VB。

VBWebProfi 给出了TLI 的提示,谢谢。不过,制定细节和编写代码需要几个小时的工作;-)

于 2017-06-09T13:55:14.620 回答
11

VBScript 本身不支持TypeNameandVarType函数之外的类型自省,它会为您提供对象的类型,但不会让您访问其内部结构。

正如其他答案所解释的那样,有一个 DLL 可以提供此功能,但它不随 Windows 一起提供,并且由于它是 Visual Studio 旧版本的一部分,现在可能没有合法的方式来获取它。

于 2013-01-14T00:00:04.620 回答
10

虽然这部分正确,但它不完整.... Google、GetObjectText_、Methods_ 和 Propeties_

引用的方法仅适用于通过 WbemScripting.SWbemLocator 对象连接到远程主机的 cimv2 命名空间时收集的对象。如果这个对象有能力在本地主机上工作,那对我来说是不明显的。

完成此操作后,您可以查询其中包含的任何类 [Win32_Services、Win32_Drives 等],并使用对象上的 For-Next 循环询问结果集中的对象,如下所示...

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

或者...

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

最后, ...

For Each oProp in oObject.Properties_
   'This will display all of an objects properties
   oProp.GetObjectText_
Next
于 2013-02-26T16:51:11.323 回答
1

如果您碰巧使用的是 HP UFT 或 QTP,请执行以下步骤:

1) 将任何版本的 MS Visual Studio 安装到您的笔记本电脑上。(不用担心许可,你不会运行 VS)

2) 重新启动计算机。

3) 启动 UFT 或 QTP,加载脚本并按 F11,(或在要检查的对象附近的任何代码处暂停)。

4) 将对象添加到 Watch 窗口。它可以是对象存储库对象或程序描述。

如果对象存在,则该对象现在将在 Watch 窗口中显示两个加号 (+),可以展开以显示所有可用的方法和属性,以及可以展开的子对象。

于 2015-11-04T19:49:37.320 回答
1

使用 TLI。类TLI.TLIApplication(from tlbinf32.dll) 可以从它们的实例中检查各种 COM 对象。在 Excel 或其他支持脚本并具有能够添加引用的脚本编辑器的 Microsoft 产品中探索 TLI 库,然后添加tlbinf32.dll. 参考文献中的名称是“Typelib 信息”。

但请注意,该 DLL 不随 Windows 一起提供。

使用InterfaceInfoFromObject()VBScript 类的方法,或者尝试ClassInfoFromObject().

Option Explicit

Dim TLI
Dim MyObject
Dim TypeInfo
Dim MemberInfo

Set TLI = CreateObject("TLI.TLIApplication")
Set MyObject = New MyClass
Set TypeInfo = TLI.InterfaceInfoFromObject(MyObject)

For Each MemberInfo In TypeInfo.Members
    WScript.Echo MemberInfo.Name
Next

Class MyClass
    Dim Name_

    Public Property Get Name
        Name = Name_
    End Property

    Public Property Let Name(ByVal Value)
        Name_ = Value
    End Property
End Class
于 2016-07-05T16:16:48.617 回答
-3

试试这个 ...

For i = 0 To webElementCount-1 Step 1

  innertextProp = myValue2(i).GetROProperty("innertext")
  print i & innertextProp
  print innertextProp

Next
于 2015-11-18T14:52:49.070 回答