-
-
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 |
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?
Also can you run a simple command like
?tli.ClassInfoFromObject(new Collection).Parent.ContainingFileshould print the path to VBE7.dll for example
When I run ?tli.ClassInfoFromObject(new Collection).Parent.ContainingFile I get
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.
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

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
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!
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!

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 thetli.TypeLibInfoFromITypeLibfails. If I have the component disabled then the code simply jumps tocleanFail. 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?