Ganz nett, aber buggy und teilweise unklar.
Erstmal ist nicht vernünftig erklärt, warum der VTable-Eintrag angepasst werden muss. Gut, wenn es nicht gemacht wird, crasht das Programm. Aber aus einem Crash leite ich nicht grundsätzlich ein VTable ummapen ab ;).
Ich habe versucht das Ganze in einer Collection ähnlichen Klasse einzusetzen und bin dabei auf ein paar Probleme gestoßen:
Das Ummappen der IEnumVariant_Next Funktion ist nicht sauber gelöst.
Wenn mehrere Objekte der Klasse instanziiert werden, und eines terminiert, wird der VTable Eintrag zurückgesetzt und danach crasht es.
Lösung:
ModulPrivate m_MapCounter As Long
Public Function MapVTable( _
ByVal ptrObject As Long, _
ByVal vIndex As Long, _
ByVal ptrNewProc As Long _
) As Long
Dim VTblOffset As Long
Dim VTblPtr As Long
Dim OldPageProtect As VirtualAllocPageFlags
' get VTable pointer
CopyMemory VTblPtr, ByVal ptrObject, 4
' offset to function
VTblOffset = VTblPtr + (vIndex * 4)
' change r/w protection
VirtualProtect VTblOffset, 4, PAGE_EXECUTE_READWRITE, OldPageProtect
' get pointer to current function
CopyMemory MapVTable, ByVal VTblOffset, 4
' replace function pointer
CopyMemory ByVal VTblOffset, ptrNewProc, 4
' reset r/w protection
VirtualProtect VTblOffset, 4, OldPageProtect, OldPageProtect
m_MapCounter = m_MapCounter + 1
End Function
Public Sub UnMapVTable( _
ByVal ptrObject As Long, _
ByVal vIndex As Long, _
ByVal ptrNewProc As Long _
)
m_MapCounter = m_MapCounter - 1
If m_MapCounter = 0 Then
MapVTable ptrObject, vIndex, ptrNewProc
End If
End Sub Klasse:Private Sub Class_Initialize()
m_ptrOldVTableEntry = MapVTable(ObjtPtrMe, 3, AddressOf _
IEnumVariantNext_Mapped)
End Sub
Private Sub Class_Terminate()
Call UnMapVTable(ObjtPtrMe, 3, m_ptrOldVTableEntry)
End Sub For Each kann nur benutzt werden, wenn die Klasse die NewEnum Property enthält.
Dann muss natürlich folgende Funktion in die "Collection" Klasse:Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
Attribute NewEnum.VB_MemberFlags = "40"
Set NewEnum = Me
End Property Dann muss natürlich die Funktion IEnumVariant_NextReDef entsprechend angepasst werden. Vielleicht für viele klar, aber auch für alle?? Hier mal mein Beispiel:Private Function IEnumVariant_NextReDef( _
ByVal celt As Long, _
rgvar As Variant, _
ByVal pceltFetched As Long _
) As IEnumVariantVB.NextRet
If Me.Count > m_IEnumIndex Then
If celt = 1 Then 'geht wahrscheinlich sowieso nicht anders seitens VB
m_IEnumIndex = m_IEnumIndex + 1
Set rgvar = Me.Item(m_IEnumIndex) 'meine Liste über die ich per Index
' zugreife
IEnumVariant_NextReDef = IEnumVariantVB.NextRet.S_OK
Else
Raise [Requested elements must be 1], "Next" 'interne Fehlerbehandlung,
' hier unwichtig
End If
Else
IEnumVariant_NextReDef = IEnumVariantVB.NextRet.S_FALSE
End If
End Function Da taucht auch gleich das nächste Problem auf. Beim nächsten For Each Durchlauf, wird m_IEnumIndex nicht zurückgesetzt. D.h. die Schleife wird nicht mehr durchlaufen. Ich dachte daher ein einfaches:Private Sub IEnumVariant_Reset()
m_IEnumIndex = 0
End Sub würde helfen. Dies wird aber leider nicht aufgerufen. Verstehe ich nicht, wofür ist das dann überhaupt gut? Damit die für mich (momentan) finale Frage:
Wie erkenne ich, dass "For Each" initialisiert wird/werden muss?
Eine spontane Idee wäre, den m_IEnumIndex auf 0 zu setzen, wenn IEnumVariant_NextReDef = IEnumVariantVB.NextRet.S_FALSE erreicht ist. Bringt aber nix, ich kann for each ja vorzeitig verlassen --> Exit For.
Vielleicht hat ja jemand eine Lösung dafür ...
Gruß
Dirk
--
?Get it right the first time |