Skip to content

Instantly share code, notes, and snippets.

@Greedquest
Created January 7, 2021 15:13
Show Gist options
  • Select an option

  • Save Greedquest/1b9b0260408d1f7a2b3b175c9a2387de to your computer and use it in GitHub Desktop.

Select an option

Save Greedquest/1b9b0260408d1f7a2b3b175c9a2387de to your computer and use it in GitHub Desktop.
'@Folder("TypeInfo")
Option Explicit
Public Type VBEReferencesObj
vTable1 As LongPtr 'To _References vtable
vTable2 As LongPtr
vTable3 As LongPtr
object1 As LongPtr
object2 As LongPtr
typeLib As LongPtr
placeholder1 As LongPtr
placeholder2 As LongPtr
refCount As LongPtr
End Type
Public Type VBETypeLibObj
vTable1 As LongPtr 'To ITypeLib vtable
vTable2 As LongPtr
vTable3 As LongPtr
Prev As LongPtr
Next As LongPtr
End Type
Sub findReferences()
Dim referencesInstancePtr As LongPtr
referencesInstancePtr = ObjPtr(Application.VBE.ActiveVBProject.References)
Debug.Assert referencesInstancePtr <> 0
' Dim referencesIUnkPtr As LongPtr
' referencesIUnkPtr = MemLongPtr(referencesInstancePtr)
' Debug.Assert referencesIUnkPtr <> 0
Dim refData As VBEReferencesObj
CopyMemory refData, ByVal referencesInstancePtr, LenB(refData)
Debug.Assert refData.vTable1 = MemLongPtr(referencesInstancePtr)
Dim typeLibInstanceTable As VBETypeLibObj
CopyMemory typeLibInstanceTable, ByVal refData.typeLib, LenB(typeLibInstanceTable)
'Create a class to iterate over the doubly linked list
Dim typeLibPtrs As New TypeLibIterator
typeLibPtrs.baseTypeLib = refData.typeLib
Dim result As TypeLibInfo
Do While typeLibPtrs.TryGetNext(result)
If result.Name = Application.VBE.ActiveVBProject.Name Then
Dim moduleTypeInfoWrapper As TypeInfo
Set moduleTypeInfoWrapper = result.TypeInfos.NamedItem("ExampleModule")
Debug.Print moduleTypeInfoWrapper.Name
Debug.Print moduleTypeInfoWrapper.Members.Count
Dim member As MemberInfo
For Each member In moduleTypeInfoWrapper.Members
On Error Resume Next 'some don't have a name defined, idk why...
Debug.Print member.Name
On Error GoTo 0
Next member
End If
Loop
End Sub
Public Function ObjectFromObjPtr(ByVal Address As LongPtr) As IUnknown
Dim result As IUnknown
MemLongPtr(VarPtr(result)) = Address
Set ObjectFromObjPtr = result
MemLongPtr(VarPtr(result)) = 0
End Function
'@Folder("TypeInfo")
Option Explicit
Private Type TIterator
currentTL As VBETypeLibObj
End Type
Private this As TIterator
Public Property Let baseTypeLib(ByVal rawptr As LongPtr)
currentTL = rawptr
ResetIteration
End Property
Private Property Let currentTL(ByVal rawptr As LongPtr)
CopyMemory this.currentTL, ByVal rawptr, LenB(this.currentTL)
End Property
Public Sub ResetIteration()
Do While this.currentTL.Prev <> 0
currentTL = this.currentTL.Prev
Loop
End Sub
Private Function NextTypeLib() As LongPtr
If this.currentTL.Next = 0 Then Err.Raise 5, Description:="We've reached the end of the line"
NextTypeLib = this.currentTL.Next
currentTL = this.currentTL.Next
End Function
'@Desccription("Gets type library com objects from list")
Public Function TryGetNext(ByRef outTypeLib As TypeLibInfo) As Boolean
On Error GoTo cleanFail
Dim tlPtr As LongPtr
tlPtr = NextTypeLib
Set outTypeLib = tli.TypeLibInfoFromITypeLib(ObjectFromObjPtr(tlPtr))
TryGetNext = True
cleanExit:
Exit Function
cleanFail:
TryGetNext = False
Set outTypeLib = Nothing
Resume cleanExit
End Function
@cristianbuse
Copy link

Hi. Had some time to play around with the code. Nice that you've managed to figure out so much. I understand exactly how many tens/hundreds of hours go into such research. Good job!

I got stuck at some point. On line 36 of the class the ObjectFromObjPtr(tlPtr) works perfectly fine but the tli.TypeLibInfoFromITypeLib fails. If I have the component disabled then the code simply jumps to cleanFail. However, if the component is enabled then the error handling is completely ignored and I get error 91 Object variable...not set. I've only tested on 64bit Win and 64bit Office. Any idea?

@Greedquest
Copy link
Author

Yeah, takes a while doesn't it!

Weird error, is that even when stepping through? I'm on 64 bit office/win too, I have had problems using the locals window but that may simply be because there's an incorrectly typed property get or something - the help file seems to suggest tli is quite lose with its types (access it following these instructions:rubberduck-vba/Rubberduck#1466 (comment))

No your problem seems to be some object not being instantiated, maybe try With New TLIApplication ? It may be a difference between our two .dll files, I just found mine on a sketchy site on the internet b/c the file isn't supposed to be redistributable and I don't have VB6. Also check the two user defined types contain something sensible before you run that line - the prev and next pointers, they might both be 0 and I haven't checked for that in the code so might be a bug. But breaking out of the error handler is very strange, sounds like a call stack corruption.

Now I think about it, accessing the extensibility library (Application.VBE) does also do weird stuff like disable the debugger, IIRC programatically adding modules and calling the code skips breakpoints. So accessing the type library may be doing something unusual. Have you enabled programmatic access to the project?

@Greedquest
Copy link
Author

Also can you run a simple command like

?tli.ClassInfoFromObject(new Collection).Parent.ContainingFile

should print the path to VBE7.dll for example

@cristianbuse
Copy link

cristianbuse commented Jan 8, 2021

When I run ?tli.ClassInfoFromObject(new Collection).Parent.ContainingFile I get

image.

Not only I've created the component step-by-step but I also manually registered using REGSVR32.

I also got the dll from a very suspicious-looking site as I don't have VB6 as well. The prev and next pointers work fine, at no given point they are both 0. Yes, I have added the Ms VB Extensibility reference and the programatic access to project. Even if I remove the extensibility reference I still get the error handling bypass.

@Greedquest
Copy link
Author

It should be something like this with the component spinning if you hit a breakpoint when the library is in use. But the library isn't used up until that line where it first fails for you so I don't imagine you'll get it to spin
image

I also used regsrvr32, I think I followed these instructions https://stackoverflow.com/a/4885637/6609896 to do something 64 bitness but honestly I don't really remember, I may just remember them because I read them. Does look like your issue is with the tli library not being registered. Got mine from https://www.dll-files.com/tlbinf32.dll.html

@Greedquest
Copy link
Author

Greedquest commented Jan 8, 2021

Anyway, looks from your question that you can navigate ITypeInfo with DispCall yourself so not the end of the world if you can't get this library to work!

@cristianbuse
Copy link

cristianbuse commented Jan 11, 2021

Thanks for sharing the links. By using the 'new' dll, it simply works. I've redone all the steps exactly the same (at least I think I did). I ran the 'regsvr32' command from the same SysWOW64 folder as before. I don't see why the dll I've used did not work unless I've done something wrong previously or if 'my' dll was corrupted. Anyway, problem solved.

It's very useful that you've figured this out. I am sure you don't plan to use it in any production code but rather to understand the mechanism behind VB. Same here. It's very helpful to have a library to inspect via Object Browser rather than reading a bunch of scattered MS documentations. I spent too many hours reading docs like the pdf version of MS-OAut. It surely helps if there's a starting point to inspect some of these structures.

Again, many thanks for sharing and helping me out to make it work! Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment