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.