Im Gegensatz zu dem, was andere gesagt haben, ist es möglich, Laufzeit-Typ-Informationen für UDTs in VB6 zu erhalten (obwohl es keine eingebaute Sprachfunktion ist). Microsofts TypeLib Informationsobjekt-Bibliothek (tlbinf32.dll) ermöglicht es Ihnen, COM-Typinformationen zur Laufzeit programmatisch zu überprüfen. Sie sollten diese Komponente bereits haben, wenn Sie Visual Studio installiert haben: Um sie zu einem bestehenden VB6-Projekt hinzuzufügen, gehen Sie zu Projekt->Referenzen und markieren Sie den Eintrag "TypeLib Information". Beachten Sie, dass Sie die Datei tlbinf32.dll im Setup-Programm Ihrer Anwendung verteilen und registrieren müssen.
Sie können UDT-Instanzen mit der TypeLib-Informationskomponente zur Laufzeit untersuchen, solange Ihre UDTs deklariert sind Public
und werden innerhalb einer Public
Klasse. Dies ist notwendig, damit VB6 COM-kompatible Typinformationen für Ihre UDTs erzeugen kann (die dann mit verschiedenen Klassen in der TypeLib-Informationskomponente aufgezählt werden können). Der einfachste Weg, diese Anforderung zu erfüllen, wäre, alle Ihre UDT's in eine öffentliche UserTypes
Klasse, die zu einer ActiveX-DLL oder ActiveX-EXE kompiliert wird.
Zusammenfassung eines Arbeitsbeispiels
Dieses Beispiel besteht aus drei Teilen:
- Teil 1 : Erstellen eines ActiveX DLL-Projekts, das alle öffentlichen UDT-Deklarationen enthält
- Teil 2 : Ein Beispiel erstellen
PrintUDT
Methode, um zu demonstrieren, wie Sie die Felder einer UDT-Instanz aufzählen können
- Teil 3 : Erstellen einer benutzerdefinierten Iterator-Klasse, mit der Sie einfach durch die Felder eines beliebigen öffentlichen UDT iterieren und Feldnamen und Werte abrufen können.
Das Arbeitsbeispiel
Teil 1: Die ActiveX-DLL
Wie bereits erwähnt, müssen Sie Ihre UDTs öffentlich zugänglich machen, um sie mit der TypeLib-Informationskomponente aufzählen zu können. Die einzige Möglichkeit, dies zu erreichen, besteht darin, Ihre UDTs in eine öffentliche Klasse innerhalb einer ActiveX-DLL oder eines ActiveX-EXE-Projekts aufzunehmen. Andere Projekte in Ihrer Anwendung, die auf Ihre UDTs zugreifen müssen, verweisen dann auf diese neue Komponente.
Um diesem Beispiel zu folgen, erstellen Sie zunächst ein neues ActiveX-DLL-Projekt und nennen Sie es UDTLibrary
.
Als nächstes benennen Sie die Class1
Klassenmodul (dies wird von der IDE standardmäßig hinzugefügt) zu UserTypes
und fügen Sie der Klasse zwei benutzerdefinierte Typen hinzu, Person
y Animal
:
' UserTypes.cls '
Option Explicit
Public Type Person
FirstName As String
LastName As String
BirthDate As Date
End Type
Public Type Animal
Genus As String
Species As String
NumberOfLegs As Long
End Type
Auflistung 1: UserTypes.cls
dient als Container für unsere UDTs
Als nächstes ändern Sie die Instanzierung Eigenschaft für die UserTypes
Klasse auf "2-PublicNotCreatable". Es gibt für niemanden einen Grund, die Klasse zu instanziieren UserTypes
Klasse direkt, weil sie einfach als öffentlicher Container für unsere UDTs fungiert.
Vergewissern Sie sich schließlich, dass die Project Startup Object
(unter Projekt->Eigenschaften ) auf "(None)" gesetzt ist und kompilieren Sie das Projekt. Sie sollten nun eine neue Datei namens UDTLibrary.dll
.
Teil 2: Aufzählung von UDT-Typ-Informationen
Jetzt ist es an der Zeit zu demonstrieren, wie wir die TypeLib Object Library verwenden können, um eine PrintUDT
método.
Beginnen Sie mit der Erstellung eines neuen Standard-EXE-Projekts und nennen Sie es nach Belieben. Fügen Sie einen Verweis auf die Datei UDTLibrary.dll
die in Teil 1 erstellt wurde. Da ich nur demonstrieren möchte, wie das funktioniert, werden wir das Fenster "Sofort" verwenden, um den Code zu testen, den wir schreiben werden.
Erstellen Sie ein neues Modul und nennen Sie es UDTUtils
und fügen Sie den folgenden Code hinzu:
'UDTUtils.bas'
Option Explicit
Public Sub PrintUDT(ByVal someUDT As Variant)
' Make sure we have a UDT and not something else... '
If VarType(someUDT) <> vbUserDefinedType Then
Err.Raise 5, , "Parameter passed to PrintUDT is not an instance of a user-defined type."
End If
' Get the type information for the UDT '
' (in COM parlance, a VB6 UDT is also known as VT_RECORD, Record, or struct...) '
Dim ri As RecordInfo
Set ri = TLI.TypeInfoFromRecordVariant(someUDT)
'If something went wrong, ri will be Nothing'
If ri Is Nothing Then
Err.Raise 5, , "Error retrieving RecordInfo for type '" & TypeName(someUDT) & "'"
Else
' Iterate through each field (member) of the UDT '
' and print the out the field name and value '
Dim member As MemberInfo
For Each member In ri.Members
'TLI.RecordField allows us to get/set UDT fields: '
' '
' * to get a fied: myVar = TLI.RecordField(someUDT, fieldName) '
' * to set a field TLI.RecordField(someUDT, fieldName) = newValue '
' '
Dim memberVal As Variant
memberVal = TLI.RecordField(someUDT, member.Name)
Debug.Print member.Name & " : " & memberVal
Next
End If
End Sub
Public Sub TestPrintUDT()
'Create a person instance and print it out...'
Dim p As Person
p.FirstName = "John"
p.LastName = "Doe"
p.BirthDate = #1/1/1950#
PrintUDT p
'Create an animal instance and print it out...'
Dim a As Animal
a.Genus = "Canus"
a.Species = "Familiaris"
a.NumberOfLegs = 4
PrintUDT a
End Sub
Listing 2: Ein Beispiel PrintUDT
Verfahren und ein einfaches Testverfahren
Teil 3: Objektorientiert werden
Die obigen Beispiele sind eine "schnelle und schmutzige" Demonstration der Verwendung der TypeLib Information Object Library zur Aufzählung der Felder eines UDT. In einem realen Szenario würde ich wahrscheinlich eine UDTMemberIterator
Klasse, die eine einfachere Iteration durch die UDT-Felder ermöglicht, zusammen mit einer Dienstprogrammfunktion in einem Modul, die eine UDTMemberIterator
für eine bestimmte UDT-Instanz. Dies würde es Ihnen ermöglichen, in Ihrem Code etwas wie das Folgende zu tun, was dem Pseudocode, den Sie in Ihrer Frage gepostet haben, viel näher kommt:
Dim member As UDTMember 'UDTMember wraps a TLI.MemberInfo instance'
For Each member In UDTMemberIteratorFor(someUDT)
Debug.Print member.Name & " : " & member.Value
Next
Es ist eigentlich nicht allzu schwer, dies zu tun, und wir können die meisten der Code aus dem wiederverwenden PrintUDT
Routine, die in Teil 2 erstellt wurde.
Erstellen Sie zunächst ein neues ActiveX-Projekt und nennen Sie es UDTTypeInformation
oder etwas Ähnliches.
Vergewissern Sie sich anschließend, dass das Startobjekt für das neue Projekt auf "(None)" gesetzt ist.
Als Erstes müssen wir eine einfache Wrapper-Klasse erstellen, die die Details der TLI.MemberInfo
Klasse aus dem aufrufenden Code zu entfernen und es einfach zu machen, den Namen und den Wert eines UDT-Feldes zu erhalten. Ich nannte diese Klasse UDTMember
. Die Instanzierung Eigenschaft für diese Klasse sollte sein PublicNotCreatable .
'UDTMember.cls'
Option Explicit
Private m_value As Variant
Private m_name As String
Public Property Get Value() As Variant
Value = m_value
End Property
'Declared Friend because calling code should not be able to modify the value'
Friend Property Let Value(rhs As Variant)
m_value = rhs
End Property
Public Property Get Name() As String
Name = m_name
End Property
'Declared Friend because calling code should not be able to modify the value'
Friend Property Let Name(ByVal rhs As String)
m_name = rhs
End Property
Auflistung 3: Die UDTMember
Wrapper-Klasse
Nun müssen wir eine Iterator-Klasse erstellen, UDTMemberIterator
die es uns ermöglichen wird, VB's For Each...In
Syntax, um die Felder einer UDT-Instanz zu iterieren. Die Instancing
Eigenschaft für diese Klasse sollte auf PublicNotCreatable
(wir werden später eine Utility-Methode definieren, die Instanzen im Namen des aufrufenden Codes erzeugt).
EDITAR: (15.2.09) Ich habe den Code noch ein wenig aufgeräumt.
'UDTMemberIterator.cls'
Option Explicit
Private m_members As Collection ' Collection of UDTMember objects '
' Meant to be called only by Utils.UDTMemberIteratorFor '
' '
' Sets up the iterator by reading the type info for '
' the passed-in UDT instance and wrapping the fields in '
' UDTMember objects '
Friend Sub Initialize(ByVal someUDT As Variant)
Set m_members = GetWrappedMembersForUDT(someUDT)
End Sub
Public Function Count() As Long
Count = m_members.Count
End Function
' This is the default method for this class [See Tools->Procedure Attributes] '
' '
Public Function Item(Index As Variant) As UDTMember
Set Item = GetWrappedUDTMember(m_members.Item(Index))
End Function
' This function returns the enumerator for this '
' collection in order to support For...Each syntax. '
' Its procedure ID is (-4) and marked "Hidden" [See Tools->Procedure Attributes] '
' '
Public Function NewEnum() As stdole.IUnknown
Set NewEnum = m_members.[_NewEnum]
End Function
' Returns a collection of UDTMember objects, where each element '
' holds the name and current value of one field from the passed-in UDT '
' '
Private Function GetWrappedMembersForUDT(ByVal someUDT As Variant) As Collection
Dim collWrappedMembers As New Collection
Dim ri As RecordInfo
Dim member As MemberInfo
Dim memberVal As Variant
Dim wrappedMember As UDTMember
' Try to get type information for the UDT... '
If VarType(someUDT) <> vbUserDefinedType Then
Fail "Parameter passed to GetWrappedMembersForUDT is not an instance of a user-defined type."
End If
Set ri = tli.TypeInfoFromRecordVariant(someUDT)
If ri Is Nothing Then
Fail "Error retrieving RecordInfo for type '" & TypeName(someUDT) & "'"
End If
' Wrap each UDT member in a UDTMember object... '
For Each member In ri.Members
Set wrappedMember = CreateWrappedUDTMember(someUDT, member)
collWrappedMembers.Add wrappedMember, member.Name
Next
Set GetWrappedMembersForUDT = collWrappedMembers
End Function
' Creates a UDTMember instance from a UDT instance and a MemberInfo object '
' '
Private Function CreateWrappedUDTMember(ByVal someUDT As Variant, ByVal member As MemberInfo) As UDTMember
Dim wrappedMember As UDTMember
Set wrappedMember = New UDTMember
With wrappedMember
.Name = member.Name
.Value = tli.RecordField(someUDT, member.Name)
End With
Set CreateWrappedUDTMember = wrappedMember
End Function
' Just a convenience method
'
Private Function Fail(ByVal message As String)
Err.Raise 5, TypeName(Me), message
End Function
Listing 4: Die UDTMemberIterator
classe.
Um diese Klasse iterierbar zu machen, ist zu beachten, dass For Each
verwendet werden kann, müssen Sie bestimmte Prozedur-Attribute in der Item
y _NewEnum
Methoden (wie in den Code-Kommentaren angegeben). Sie können die Prozedur-Attribute über das Menü "Werkzeuge" ändern (Werkzeuge->Prozedur-Attribute).
Schließlich benötigen wir eine Nutzenfunktion ( UDTMemberIteratorFor
im allerersten Codebeispiel in diesem Abschnitt), die eine UDTMemberIterator
für eine UDT-Instanz, die wir dann iterieren können mit For Each
. Erstellen Sie ein neues Modul namens Utils
und fügen Sie den folgenden Code hinzu:
'Utils.bas'
Option Explicit
' Returns a UDTMemberIterator for the given UDT '
' '
' Example Usage: '
' '
' Dim member As UDTMember '
' '
' For Each member In UDTMemberIteratorFor(someUDT) '
' Debug.Print member.Name & ":" & member.Value '
' Next '
Public Function UDTMemberIteratorFor(ByVal udt As Variant) As UDTMemberIterator
Dim iterator As New UDTMemberIterator
iterator.Initialize udt
Set UDTMemberIteratorFor = iterator
End Function
Auflistung 5: Die UDTMemberIteratorFor
Nutzenfunktion.
Kompilieren Sie schließlich das Projekt und erstellen Sie ein neues Projekt, um es zu testen.
Fügen Sie in Ihrem Testprojekt einen Verweis auf das neu erstellte UDTTypeInformation.dll
und die UDTLibrary.dll
die Sie in Teil 1 erstellt haben, und probieren Sie den folgenden Code in einem neuen Modul aus:
'Module1.bas'
Option Explicit
Public Sub TestUDTMemberIterator()
Dim member As UDTMember
Dim p As Person
p.FirstName = "John"
p.LastName = "Doe"
p.BirthDate = #1/1/1950#
For Each member In UDTMemberIteratorFor(p)
Debug.Print member.Name & " : " & member.Value
Next
Dim a As Animal
a.Genus = "Canus"
a.Species = "Canine"
a.NumberOfLegs = 4
For Each member In UDTMemberIteratorFor(a)
Debug.Print member.Name & " : " & member.Value
Next
End Sub
Listing 6: Ausprobieren der UDTMemberIterator
classe.
0 Stimmen
Ist dieser benutzerdefinierte Typ eine Klasse/Typ?
0 Stimmen
Ups, Entschuldigung. Ich habe erst nach dem Posten bemerkt, dass der Name der Funktion PrintUDT ist. Es sollte also ein Typ sein (eine Art Struktur). Richtig?
0 Stimmen
Verwandt: stackoverflow.com/questions/3222080/