Vra

Ek het'n gevoel die antwoord hierop is gaan om te wees "nie moontlik nie", maar ek sal dit gee'n skoot...Ek is in die onbenydenswaardige posisie van die wysiging van'n nalatenskap VB6 app met'n paar verbeterings.Die omskakeling na'n slimmer taal is nie'n opsie nie.Die app maak staat op'n groot versameling van die gebruiker gedefinieerde tipes om data te skuif rond.Ek wil graag om te definieer'n gemeenskaplike funksie wat jy kan neem'n verwysing na enige van hierdie tipes en uittreksel van die data vervat.
In die pseudo-kode, hier is wat ek is op soek na:

Public Sub PrintUDT ( vData As Variant )
  for each vDataMember in vData
    print vDataMember.Name & ": " & vDataMember.value 
  next vDataMember 
End Sub

Dit lyk soos hierdie inligting moet beskikbaar wees om COM iewers...Enige VB6 ghoeroes daar buite sorg te neem van'n skoot?

Dankie,

Dan

Was dit nuttig?

Oplossing

In teenstelling met wat ander sê, dit IS moontlik om te kry run-time tipe inligting vir die UDT is in VB6 (hoewel dit is nie'n ingeboude in die taal-funksie).Microsoft se TypeLib Inligting Voorwerp Biblioteek (tlbinf32.dll) kan jy om te programmaties inspekteer COM tipe inligting op'n run-time.Jy moet reeds hierdie komponent as jy Visual Studio geïnstalleer:toe te voeg tot'n bestaande VB6 projek, gaan na Projek->Verwysings en check die inskrywing gemerk "TypeLib Inligting." Let daarop dat jy sal hê om te versprei en te registreer tlbinf32.dll in jou aansoek se opstel van die program.

Jy kan inspekteer UDT gevalle met behulp van die TypeLib Inligting komponent op run-tyd, so lank as jou UDT se is verklaar Public en gedefinieer word binne'n Public die klas.Dit is nodig in orde te maak VB6 genereer COM-versoenbaar tipe inligting vir jou UDT se (wat kan dan word vervat is met die verskillende klasse in die TypeLib Inligting komponent).Die maklikste manier om te voldoen aan hierdie vereiste sou wees om te sit al jou UDT is in'n openbare UserTypes klas wat sal saamgestel word in'n ActiveX DLL of ActiveX EXE.

Opsomming van'n werkende voorbeeld

Hierdie voorbeeld bevat drie dele:

  • Deel 1:Die skep van'n ActiveX DLL projek wat sal bevat al die openbare verklarings UDT
  • Deel 2:Skep'n voorbeeld PrintUDT metode te demonstreer hoe jy kan opnoemen die velde van'n UDT geval
  • Deel 3:Die skep van'n persoonlike iterator klas wat toelaat dat jy maklik itereer deur die velde van enige openbare UDT en kry veld name en waardes.

Die werkende voorbeeld

Deel 1:Die ActiveX DLL

Soos ek reeds genoem het, jy nodig het om te maak jou UDT se openbare-toeganklik te maak ten einde om hulle te opnoemen die gebruik van die TypeLib Inligting komponent.Die enigste manier om dit te bereik is om te sit jou UDT is in'n openbare klas binne'n ActiveX DLL of ActiveX EXE-projek.Ander projekte in jou aansoek dat die behoefte om toegang tot jou UDT se sal dan verwysing van hierdie nuwe komponent.

Om te volg saam met hierdie voorbeeld, begin deur die skep van'n nuwe ActiveX DLL projek en noem dit UDTLibrary.

Volgende, die naam van die Class1 klas module (hierdie is bygevoeg by verstek deur die IDE) te UserTypes en voeg twee gebruiker-gedefinieerde tipes van die klas, Person en 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

Listing 1: UserTypes.cls dien as'n houer vir ons UDT se

Volgende, verander die Instancing eiendom vir die UserTypes klas te "2-PublicNotCreatable".Daar is geen rede vir enige iemand om te instansieer die UserTypes klas direk, want dit is net optree as'n openbare houer vir ons UDT is.

Ten slotte, maak seker dat die Project Startup Object (onder Projek->Eienskappe) is ingestel om te "(Geen)" en stel die projek.Jy moet nou'n nuwe lêer genaamd UDTLibrary.dll.

Deel 2:Opname UDT Tipe Inligting

Nou is dit tyd om te demonstreer hoe ons dit kan gebruik TypeLib Voorwerp Biblioteek te implementeer'n PrintUDT metode.

Eerste, begin deur die skep van'n nuwe Standaard EXE-projek en noem dit wat jy wil.Voeg'n verwysing na die lêer UDTLibrary.dll dit is geskep in Deel 1.Sedert ek wil net om te demonstreer hoe dit werk, sal ons gebruik maak van die Onmiddellike venster om die toets van die kode wat ons sal skryf.

Skep'n nuwe Module, noem dit UDTUtils en voeg die volgende kode om dit te:

'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

Lys 2:'n voorbeeld PrintUDT metode en'n eenvoudige toets metode

Deel 3:Maak dit Objek-Georiënteerde

Die bogenoemde voorbeelde verskaf'n "vinnige en vuil" demonstrasie van hoe om te gebruik die TypeLib Inligting Voorwerp Biblioteek te opnoemen die velde van'n UDT.In'n werklike-wêreld scenario, sou ek waarskynlik die skep van'n UDTMemberIterator klas wat sal toelaat dat jy meer maklik te itereer deur die velde van die UDT, saam met'n nut funksie in'n module wat'n UDTMemberIterator vir'n gegewe UDT geval.Dit sal toelaat dat jy om iets te doen soos die volgende in jou kode, wat is veel nader aan die pseudo-kode wat jy geplaas in jou vraag:

Dim member As UDTMember 'UDTMember wraps a TLI.MemberInfo instance'

For Each member In UDTMemberIteratorFor(someUDT)
   Debug.Print member.Name & " : " & member.Value
Next

Dit is eintlik nie te moeilik om dit te doen, en ons kan weer gebruik die meeste van die kode van die PrintUDT roetine geskep in Deel 2.

Eerste, die skep van'n nuwe ActiveX projek en noem dit UDTTypeInformation of iets soortgelyk.

Volgende, maak seker dat die Startup Voorwerp vir die nuwe projek is ingestel op "(Nie)".

Die eerste ding om te doen is om te skep'n eenvoudige wrapper klas wat sal steek die besonderhede van die TLI.MemberInfo klas van die roeping kode en maak dit maklik om te kry'n UDT se veld se naam en waarde.Ek het hierdie klas UDTMember.Die Instancing eiendom vir hierdie klas moet wees 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

Lys 3:Die UDTMember wrapper klas

Nou het ons nodig het om te skep'n iterator klas, UDTMemberIterator, wat sal toelaat dat ons om te gebruik VB se For Each...In sintaksis te itereer die velde van'n UDT geval.Die Instancing eiendom vir hierdie klas moet ingestel word om PublicNotCreatable (ons sal definieer'n nut metode later sal skep gevalle namens van die roeping van die kode).

EDIT: (2/15/09) ek het skoongemaak die kode'n bietjie meer.

'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

Lys 4:Die UDTMemberIterator die klas.

Let daarop dat ten einde te maak van hierdie klas iterable so dat For Each kan gebruik word met dit, sal jy het om te stel sekere Prosedure Eienskappe op die Item en _NewEnum metodes (soos in die kode kommentaar).Jy kan dit verander die Proses die Eienskappe van die Kieslys (Tools->Prosedure Eienskappe).

Ten slotte, ons moet'n nut funksie (UDTMemberIteratorFor in die heel eerste kode voorbeeld in hierdie afdeling) wat sal die skep van'n UDTMemberIterator vir'n UDT byvoorbeeld, wat kan ons dan itereer met For Each.Skep'n nuwe module genoem Utils en voeg die volgende kode:

'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

Notering 5:Die UDTMemberIteratorFor nut funksie.

Ten slotte, stel die projek en die skep van'n nuwe projek, om dit uit te toets.

In jou toets projet, voeg'n verwysing na die nuut-geskepte UDTTypeInformation.dll en die UDTLibrary.dll geskep in Deel 1 en probeer om uit die volgende kode in'n nuwe module:

'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

Notering 6:Die toets uit die UDTMemberIterator die klas.

Ander wenke

@Dan,

Dit lyk soos jou probeer om te gebruik RTTI van'n UDT.Ek dink nie jy kan regtig kry dat die inligting sonder om te weet oor die UDT voor die run-time.Te kry wat jy begin om te probeer:

Begrip UDTs
As gevolg van nie met hierdie besinning vermoë.Ek sou my eie RTTI om my UDTs.

Te gee jy'n basislyn.Probeer om hierdie:

Type test
    RTTI as String
    a as Long
    b as Long 
    c as Long
    d as Integer
end type

Jy kan skryf'n program wat sal maak elke bron lêer en voeg Die RTTI met die naam van die tipe van die UDT.Waarskynlik sou beter wees om te sit al die UDTs in'n algemene lêer.

Die RTTI sou iets soos hierdie:

"String:Lang:Lang:Heelgetal"

Met behulp van die geheue van die UDT jy kan pak die waardes.

As jy verander al jou Tipes om Klasse.Jy het opsies.Die groot slaggat van die verandering van'n tipe van'n klas is wat jy het om te gebruik om die nuwe keyworld.Elke keer as daar'n verklaring van'n tipe veranderlike voeg nuwe.

Dan kan jy gebruik maak van die variant navraag of CallByName.VB6 nie anytype van besinning, maar jy kan maak lyste van geldige velde en toets om te sien of hulle teenwoordig is byvoorbeeld

Die Klas Toets het die volgende

Public Key As String
Public Data As String

Jy kan dan die volgende doen

Private Sub Command1_Click()
    Dim T As New Test 'This is NOT A MISTAKE read on as to why I did this.
    T.Key = "Key"
    T.Data = "One"
    DoTest T
End Sub

Private Sub DoTest(V As Variant)
    On Error Resume Next
    Print V.Key
    Print V.Data
    Print V.DoesNotExist
    If Err.Number = 438 Then Print "Does Not Exist"
    Print CallByName(V, "Key", VbGet)
    Print CallByName(V, "Data", VbGet)
    Print CallByName(V, "DoesNotExist", VbGet)
    If Err.Number = 438 Then Print "Does Not Exist"
End Sub

As jy probeer om te gebruik om'n veld wat nie bestaan dan fout 438 sal opgewek word.CallByName jy kan gebruik om die snare te bel die veld en metodes van'n klas.

Wat VB6 doen wanneer jy verklaar Dowwe as die Nuwe is baie interessant en sal grootliks verminder foute in hierdie omskakeling.Jy sien hierdie

Dim T as New Test

is nie behandel word nie presies dieselfde as

Dim T as Test
Set T = new Test

Byvoorbeeld, dit sal werk

Dim T as New Test
T.Key = "A Key"
Set T = Nothing
T.Key = "A New Key"

Dit gee'n fout

Dim T as Test
Set T = New Test
T.Key = "A Key"
Set T = Nothing
T.Key = "A New Key"

Die rede hiervoor is dat in die eerste voorbeeld VB6 vlae T so dat wanneer'n lid is verkry dit kontroleer of die T is niks.As dit is dit sal outomaties skep'n nuwe weergawe van die Toets Klas en dan wys die veranderlike.

In die tweede voorbeeld VB nie by hierdie gedrag.

In die meeste projek het ons streng maak seker ons gaan Dowwe T as Toets, Stel T = Nuwe Toets.Maar in jou geval, want jy wil om te skakel Tipes in Klasse met die minste hoeveelheid van die newe-effekte met behulp van Dowwe T as Nuwe Toets is die pad om te gaan.Dit is omdat die Dowwe as Nuwe veroorsaak dat die veranderlike na te boots die manier tipes werk meer nou.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top