VB6/VBA 遍历所有预先声明的 class 对象
VB6/VBA Iterate through all predeclared class objects
我可以动态迭代所有预先声明的对象吗?
这是我已经处理了一段时间的问题。理想情况下,我会遍历所有 类 并检查它们是否实现了某些接口。如果他们这样做,则对他们执行一些代码。
目前我必须提供一些 类 的数组来执行,例如:
ClassesToCheck = Array(Task_Class1,Task_Class2,Task_Class3,Task_Class4, ...)
Dim klass as object
For each klass in ClassesToCheck
if klass.implements(ITask) then
Call klass.execute()
end if
next
在理想的世界里,我会做这样的事情:
Dim klass as object
For each klass in GET_PREDECLARED_CLASS_OBJECTS_FROM_MEMORY()
if klass.implements(ITask) then
Call klass.execute()
end if
next
我不认为有任何简单的方法可以做到这一点,但我已经对 VBA 运行时内存做了一些 research/exploration...我认为这应该是可能的并且已经使用 VBA6.DLL 找到了一些 VB6 示例,但是,不幸的是,Microsoft Office 本机未提供此 DLL。但是,VBA6.DLL 可能是 'compiled into' Microsoft Office 本身。所以 methods/fields 也应该保存在内存中的某个地方,你只需要知道他们在哪里使用指针数学(这是我的理论)
我想没有人有这方面的经验吧?
我不知道如何利用 tlbinf32.dll
,直接从内存中获取 class 信息,但是,如果您坚持使用当前方法,但使用对象集合而不是对象数组,您可以执行以下操作:
Private ConditionalExecution()
Dim PredeclaredClasses As Collection
Set PredeclaredClasses = New Collection
PredeclaredClasses.Add Task_Class1
PredeclaredClasses.Add Task_Class2
PredeclaredClasses.Add Task_Class3
PredeclaredClasses.Add Task_Class4
ExecuteIfImplementsInterface PredeclaredClasses
End Sub
Private Sub ExecuteIfImplementsInterface(ByVal Classes As Collection)
Dim klass as object
For each klass in classes
If TypeOf klass Is ITask Then klass.execute()
next
End Sub
当然,这将取决于这些 classes 必须通过 "dimming" 作为它们正在实现的接口实例化的事实,如下所示:
Dim bar As ITask
Set bar = New Task_Class1
旁注:如果您想知道,是的,我不擅长想出方法名称。
VB_PredeclaredId
属性使您的 Class1
标识符自动引用该名称的全局范围对象,例如UserForm1
是 class 模块的名称(一个有设计器,但那部分是无关紧要的), 和 它是生成的全局自动魔法对象的名称通过 VBA 在运行时,编译器知道 Class1.DoStuff
是合法的 因为 它知道 Class1
已将 VB_PredeclaredId
设置为 True
.
感谢 Wayne Phillips (vbWatchDog) 和其他贡献者的工作和贡献,Rubberduck taps into this internal API。
如链接代码 (C#) 所示,通过将指针转换为结构,您可以从其 References
集合中获取 VBA 项目的 ITypeLib
使用此特定布局:
[StructLayout(LayoutKind.Sequential)]
internal struct VBEReferencesObj
{
IntPtr _vTable1; // _References vtable
IntPtr _vTable2;
IntPtr _vTable3;
IntPtr _object1;
IntPtr _object2;
public IntPtr _typeLib; // <--- here's the pointer you want
IntPtr _placeholder1;
IntPtr _placeholder2;
IntPtr _refCount;
}
在 VBA 中,这将是用户定义的 Type
,可能如下所示:
Public Type VBEReferencesObj
vTable1 As LongPtr
vTable2 As LongPtr
vTable3 As LongPtr
object1 As LongPtr
object2 As LongPtr
typelibPointer As LongPtr '<~ only this one matters
placeholder1 As LongPtr
placeholder2 As LongPtr
refCount As LongPtr
End Type
获得指向 ITypeLib
的指针后,您应该能够获得 VBA 项目的类型库。
从那里,您需要迭代类型,并从那里确定类型的 TYPEFLAGS
是否启用了 TYPEFLAG_PREDECLID
(我们这样做 here)。
显然这是很多极易崩溃的试错编码,我不建议这样做,但无论如何,它是可能,如果不建议的话。
随时研究 Rubberduck.VBEEditor.ComManagement.TypeLibs 命名空间。
我可以动态迭代所有预先声明的对象吗?
这是我已经处理了一段时间的问题。理想情况下,我会遍历所有 类 并检查它们是否实现了某些接口。如果他们这样做,则对他们执行一些代码。
目前我必须提供一些 类 的数组来执行,例如:
ClassesToCheck = Array(Task_Class1,Task_Class2,Task_Class3,Task_Class4, ...)
Dim klass as object
For each klass in ClassesToCheck
if klass.implements(ITask) then
Call klass.execute()
end if
next
在理想的世界里,我会做这样的事情:
Dim klass as object
For each klass in GET_PREDECLARED_CLASS_OBJECTS_FROM_MEMORY()
if klass.implements(ITask) then
Call klass.execute()
end if
next
我不认为有任何简单的方法可以做到这一点,但我已经对 VBA 运行时内存做了一些 research/exploration...我认为这应该是可能的并且已经使用 VBA6.DLL 找到了一些 VB6 示例,但是,不幸的是,Microsoft Office 本机未提供此 DLL。但是,VBA6.DLL 可能是 'compiled into' Microsoft Office 本身。所以 methods/fields 也应该保存在内存中的某个地方,你只需要知道他们在哪里使用指针数学(这是我的理论)
我想没有人有这方面的经验吧?
我不知道如何利用 tlbinf32.dll
,直接从内存中获取 class 信息,但是,如果您坚持使用当前方法,但使用对象集合而不是对象数组,您可以执行以下操作:
Private ConditionalExecution()
Dim PredeclaredClasses As Collection
Set PredeclaredClasses = New Collection
PredeclaredClasses.Add Task_Class1
PredeclaredClasses.Add Task_Class2
PredeclaredClasses.Add Task_Class3
PredeclaredClasses.Add Task_Class4
ExecuteIfImplementsInterface PredeclaredClasses
End Sub
Private Sub ExecuteIfImplementsInterface(ByVal Classes As Collection)
Dim klass as object
For each klass in classes
If TypeOf klass Is ITask Then klass.execute()
next
End Sub
当然,这将取决于这些 classes 必须通过 "dimming" 作为它们正在实现的接口实例化的事实,如下所示:
Dim bar As ITask
Set bar = New Task_Class1
旁注:如果您想知道,是的,我不擅长想出方法名称。
VB_PredeclaredId
属性使您的 Class1
标识符自动引用该名称的全局范围对象,例如UserForm1
是 class 模块的名称(一个有设计器,但那部分是无关紧要的), 和 它是生成的全局自动魔法对象的名称通过 VBA 在运行时,编译器知道 Class1.DoStuff
是合法的 因为 它知道 Class1
已将 VB_PredeclaredId
设置为 True
.
感谢 Wayne Phillips (vbWatchDog) 和其他贡献者的工作和贡献,Rubberduck taps into this internal API。
如链接代码 (C#) 所示,通过将指针转换为结构,您可以从其 References
集合中获取 VBA 项目的 ITypeLib
使用此特定布局:
[StructLayout(LayoutKind.Sequential)]
internal struct VBEReferencesObj
{
IntPtr _vTable1; // _References vtable
IntPtr _vTable2;
IntPtr _vTable3;
IntPtr _object1;
IntPtr _object2;
public IntPtr _typeLib; // <--- here's the pointer you want
IntPtr _placeholder1;
IntPtr _placeholder2;
IntPtr _refCount;
}
在 VBA 中,这将是用户定义的 Type
,可能如下所示:
Public Type VBEReferencesObj
vTable1 As LongPtr
vTable2 As LongPtr
vTable3 As LongPtr
object1 As LongPtr
object2 As LongPtr
typelibPointer As LongPtr '<~ only this one matters
placeholder1 As LongPtr
placeholder2 As LongPtr
refCount As LongPtr
End Type
获得指向 ITypeLib
的指针后,您应该能够获得 VBA 项目的类型库。
从那里,您需要迭代类型,并从那里确定类型的 TYPEFLAGS
是否启用了 TYPEFLAG_PREDECLID
(我们这样做 here)。
显然这是很多极易崩溃的试错编码,我不建议这样做,但无论如何,它是可能,如果不建议的话。
随时研究 Rubberduck.VBEEditor.ComManagement.TypeLibs 命名空间。