Introduction
Probably, everyone knows how it is delightful to create a watch for an object, inspecting its properties and so on.. Anyway, you've probably noticed that VB forgets among different sessions what you were watching, so the obvious idea is to build up a custom debug form which shows what you need. But there is a problem, which is that you can inspect easily default types, but for COM objects it's a completely different matter. My small and stupid control takes care of this thing, as it lets you to inspect all run-time properties of a generic COM object using a TreeView control.
Using the code
The usage is quite simple:
- You create a form
- You add an
ObjView control to the form
- You start your program,
- While running, you can:
- Add an object to inspection through the
Add method of the control. Note that you need to provide a string which will identify your object in a unique way.
- Remove an object from the inspection using the
Remove method.
- Refresh the values of all the objects under inspection through the
Refresh method.
- Refresh a single object through the
RefreshOBJ method, which requires as a first parameter a reference to the object, and as a second, the identifier of the object which you gave to the control when adding the object itself.
All the inspection stuff is done using the TypeLib Information library (tlbinf32.dll), which exposes various classes and methods. Note that a VB COM object cannot expose more than an interface for each class (or at least I did not find any object which does this thing), the idea is simple:
- Get the Interface descriptor of the given object
Dim tliApp as TLI.TLIApplication
Dim tliIrf as TLI.InterfaceInfo
Set tliApp = new TLI.TLIApplication
Set tliIrf = tliApp.InterfaceInfoFromObject(< object variable > )
- Examine the
Members collection of the tliIrf variable, which contains all the public members exposed from the class Dim tliMI as TLI.MemberInfo For Each tliMi In tliIrf.Members ... Next
Each MemberInfo class contains data which refers to the public members (functions, subs, variables, properties ...) of the selected interface. The relevant fields are:
Name: it is the name of the member, which is used to retrieve the value.
Parameters: it is a collection of ParameterInfo which contains info about the parameters of the selected method.
InvokeKind: it is an enumerated type InvokeKinds which tells what kind of member is it.
ReturnType: it is a VarTypeInfo class which contains info about the returned type
- As I wanted to create a debug-like object, I did the following assumptions:
- If it is a collection, or an object which implements a collection, analyze each item. To discover if it a collection, the control uses the following procedure:
Private Function IsCollection(v As Variant) As Boolean
Dim tlApp As TLI.TLIApplication
Dim tlIrf As TLI.InterfaceInfo
Dim tlmi As TLI.MemberInfo
Dim Obj As Object
If IsObject(v) Then
If TypeName(v) = "Collection" Then
IsCollection = True
End If
Set Obj = v
Set tlApp = New TLI.TLIApplication
Set tlIrf = tlApp.InterfaceInfoFromObject(Obj)
For Each tlmi In tlIrf.Members
If LCase$(tlmi.Name) = "item" Then
IsCollection = True
Exit For
End If
Next
End If
Set tlmi = Nothing
Set tlIrf = Nothing
Set tlApp = Nothing
End Function
So, if the object does not have an Item method, it will not be recognized as a collection, which is coherent with the VB behavior.
- If it is not a collection, every member is parsed using the following tables:
InvokeKind: if it is not a Get (INVOKE_PROPERTYGET), discard it.
- If it has 1 parameter or more, discard it.
- As regards the return type:
- It uses the
CallByName statement to get the current value of the selected member.
- If it is an object, it will recurse over this object.
- If not, it will display a string Member Name = Member Value
Points of interest
An interesting point is how the TypeInfo library manages enumerated types. It does not treat them as long, but instead it fills the TypeInfo.Members collection of the ReturnType member with as much items as the enumerated type has, so each item is a TypeInfo with:
Value the numeric value of the enumeration item.
Name the symbolic name associated to it.
So, using a For ... Each control block, it's easy to find the symbolic name which represents a given numeric value, which usually is also better understood by the developer.
History
- 12/10/2003: First version released to the public.