Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Java Reflection In VBA? Iterate through a class' members? 1

Status
Not open for further replies.

tcl

IS-IT--Management
Mar 5, 2001
15
US
I've been investigating using object oriented programming techniques with VBA (please don't ask why) and ran into a number of problems; one I haven't solved yet: Is there a way to iterate through a class module's members? For example, let's say a create a class named person, that has an ID, FirstName, and LastName private members. If there a way for me to ask the class for a list (collection, whatever) of it's members? and then, better yet, ask the class for each of those members current values by member name? This would work sort of like the DAO.Recordset Field method, where you specify an index or the name of the Field to access the field's value. Any suggestions?
 
tcl,

I am not an expert in this area, but I cannot quite tell if you are trying to iterate through the private data members of a user-defined class or through a collection.

If you are trying to generate a list of all the values of the user-defined class, I am not aware of any command/function that does that. You would need to create a method or procedure that produced the desired results. That doesn't mean there isn't a way, just that I'm not aware of it.

If you are attempting to use a for...each loop on a user-defined collection, there is a hidden index you have to setup in order to allow that to occur. I would have to go back to reference (that I don't have with me right this second). I have never used it in VBA, only VB so not sure it's possible in VBA...

HTH - let me know. MM
 
Thanks, but no, I'm not trying to iterate through a collection. I am trying to iterate through the private members of a class. I am willing to manually create an array of members for each class. Thinking about this a bit more, maybe there is a way. What I originally wanted to do was to map a field from a resultset to the member of the same name. Something like this:
n = 1
for each m in members
me.Member(rs.fields(n).Name) = rs.fields(n).Value
n = n + 1
next

but maybe I could do it the other way around:
n = 1
for each m in members
me.Member(n).Value = rs.fields(me.Memeber(n).Name).Value
n = n + 1
next

I think I can write a set of functions that will return the name and value of a member. I'll try it out and let you know.

Except, I think that'll have to be a function, instead of a property, like this:
n = 1
for each m in members
me.MemberNLet(n, me.MemberNName(n), rs.fields(me.Memeber(n).Name).Value)
n = n + 1
next

MemberNLet(n as integer, memberName as String, memberValue as variant)
 
Hiya,

if the class module's part of you VBA project, try playing around with the VBProject and CodePane objects. You'll have to add a reference to Microsoft Visual Basic for Application Extensibility. Dunno if it'll allow you to directly access the member names, but you can loop thru & pick out the subs, properties & methods


Cheers
Nikki


 
tcl,

It almost sounds to me like what you want to do is load up a Dictionary object with a number of key/item pairs and then reference each value by it's name...? It's the VB version of the associative array: you give the name (key), you get back the value. Just a suggestion.

Good luck. MM
 
You need to get hold of the TypeLib Information library ActiveX object (TLBINF32.DLL). This ships with Visual Studio, but it is a redistributable file and so you should be able to find it available for download somewhere (you might also want TLBINF32.CHM, which is the help file).

Using the above mentioned library will allow you to do exactly what you want.

Here is an example (it assumes you have got hold of the library, registered it, and added a reference to it). It is a direct lift from one of my VB apps, and has been tested in Word:
[tt]
Option Explicit

' Method that walks through the properties of an object
' In this example we happen to only be interested in Function properties
Public Sub PropertyWalk(ByRef QueriedObject As Object)

Dim TLIApp As TLIApplication
Dim TLInfo As TLI.InterfaceInfo

Dim MI As MemberInfo
Dim vParam As Variant
Dim ParamCount As Long


Set TLIApp = New TLIApplication
' grab info from object
Set TLInfo = TLI.InterfaceInfoFromObject(QueriedObject)

' Loop through attribute members of the object
For Each MI In TLInfo.Members

Select Case MI.InvokeKind
Case INVOKE_CONST 'Constant
Case INVOKE_EVENTFUNC ' Event
Case INVOKE_FUNC ' sub/function
' AT this point MI contains all the info you need about a class
Debug.Print MI.Name & "(";
For ParamCount = 1 To MI.Parameters.Count
If ParamCount <> 1 Then Debug.Print &quot;,&quot;;
If MI.Parameters(ParamCount).Optional Then Debug.Print &quot;Optional &quot;;
Debug.Print MI.Parameters(ParamCount);
Next
Debug.Print &quot;)&quot;
Case INVOKE_PROPERTYGET ' Get
Case INVOKE_PROPERTYPUT ' Let
Case INVOKE_PROPERTYPUTREF ' Set
Case INVOKE_UNKNOWN ' Dunno
Case Else 'Oops
End Select
Next
End Sub
[/tt]
And here is an example of calling it. You can just type this in the Immediate window:
[tt]
PropertyWalk ActiveDocument
[/tt]
or
[tt]
PropertyWalk Application.AddIns
[/tt]
Naturally the parameter you pass to the method can be an instance of one of your own classes
 
Thanks very much to all who answered. It turns out that Strongm pointed me to what I was looking for. His reply recommending the tlbinf32.dll and this article *Self-aware Data Wrapper Objects with VB* by Syd Egan provided me with what I needed.

I did explore Nikita6003's suggestion of the VBProject and CodePane objects. It seemed like a good idea but to get a list of a class module's members would require screen scrapping, which I hate to have to do because it's so prone to error. Plus, I knew that there was a way since VB's intellisense and the Object Explorer were able to display the information to me.

mmandk9 was correct that it did sound like a pair of functions accessing a dictionary object might have worked, and that was what I was exploring before I made the first post. The problem I ran into with that was how can you reference a class module's members by name? I.e., how can you reference a class module's members like a recordset field collection: rs.fields('fieldName'). The paper at the link above pointed me to the CallByName function which does exactly that.

Finally, with strongm's code and the tlbinf32 help file, I was able to write a pair of get and set (let, in VB) routines that assign the fields of a recordset to a class module's corresponding members. I'll paste them below. BTW, the tlbinf32 dll is a really nice object, but the help file is a crime. Fortunately, there's sample code on the web of *roll-your-own* object explorer's.

Why did I want to write these routines? Because I've had trouble in the past waiting for a database or stored procedure to catch up to our development efforts. The programmers are blasting ahead adding new functionality but the DBA's are backed-up doing something else. I'd prefer that the programmer's go ahead and dummy up a missing field until it finally shows up in the recordset. They can handle a missing field in an error routine and still provide a complete object back to their calling routine. Make sense?

Here are the routines:
Code:
Sub SetMembersFromRS(rs As DAO.Recordset, o As Object)
    ' Set all of the classes members to the corresponding fields of the recordset.
    Dim tl As TLIApplication
    Dim tli As tli.InterfaceInfo
    Dim mi As tli.MemberInfo
    
    Set tl = New TLIApplication
    Set tli = tl.InterfaceInfoFromObject(o)
    
    For Each mi In tli.Members
        If mi.InvokeKind = INVOKE_PROPERTYPUT And mi.ReturnType <> VT_EMPTY Then
            CallByName o, mi.Name, VbLet, rs.Fields(mi.Name)
        End If
    Next
    
    Set tl = Nothing
    Set tli = Nothing
    Set mi = Nothing
End Sub

Sub SetFieldsFromClass(rs As DAO.Recordset, o As Object)
    Dim tl As TLIApplication
    Dim tli As tli.InterfaceInfo
    Dim mi As tli.MemberInfo
    
    Set tl = New TLIApplication
    Set tli = tl.InterfaceInfoFromObject(o)

    ' I could approach this from either the recordset or class member direction.
    ' I am not sure which is the best way (maybe both!) but not I am thinking that
    ' from the class members is correct for now, because the synchronization usually
    ' gets messed up from a missing database field, not a missing class member.
    For Each mi In tli.Members
        If mi.InvokeKind = INVOKE_PROPERTYGET And mi.ReturnType <> VT_EMPTY Then
            Debug.Print mi.Name & &quot;  = &quot; & CallByName(o, mi.Name, VbGet)
            rs.Fields(mi.Name) = CallByName(o, mi.Name, VbGet)
        End If
    Next

    Set tl = Nothing
    Set tli = Nothing
    Set mi = Nothing
End Sub

Thanks again for your help.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top