我正在使用VBIDE API,并且不能假设主机应用程序是Excel或任何Office应用程序。
所以我知道的是,我正在查看VBComponent
,Type
是vbext_ct_document
。
在VBE的立即窗格中,我可以得到这个输出:
?TypeName(Application.VBE.ActiveVBProject.VBComponents("Sheet1"))
VBComponent
?TypeName(Sheet1)
Worksheet
但是Sheet1
对象只存在于运行时环境中,所以如果我是C#加载项,我甚至都看不到它。
唯一可以接近我需要的东西是通过组件的Parent
和Next
属性:
?TypeName(Application.VBE.ActiveVBProject.VBComponents("Sheet1").Properties("Parent").Object)
Workbook
?TypeName(Application.VBE.ActiveVBProject.VBComponents("Sheet1").Properties("Next").Object)
Worksheet
这让我得到了我所追求的类型名称......但错误的组件!对于ThisWorkbook
,这是顶级文档对象,我将Application
对象作为Parent
:
?TypeName(Application.VBE.ActiveVBProject.VBComponents("ThisWorkbook").Properties("Parent").Object)
Application
该方法可能有用,但仅当我硬编码特定于主机的逻辑时才知道哪个组件具有“应用程序”类型的“父”属性当主机应用程序是Excel时,它是一个Workbook
实例...并且不能保证其他主机中的其他文档模块甚至会有“父”属性,所以我很难过。
我打开任何 - 从p / invoke调用和低级COM“反射”魔法(ITypeInfo
类型的魔法)到......来......我不知道 - unsafe
代码带有时髦的指针,需要// here be dragons
条评论 - 任何主要都可能最终成为可行的解决方案。
AFAIK VBE加载项与主机位于同一进程中,因此某处指向ThisWorkbook
和Sheet1
以及其他任何文档类型{{ 1>}在VBA项目中。
VBComponent
我想我只需要抓住指针以某种方式,我将成为我需要的地方。
答案 0 :(得分:7)
不幸的是,vbComponent属性集合的值/对象只是CoClass实例值的反映,因此它们在所有VBA主机中都不可靠。例如,您无法知道 Parent
属性将存在于Properties集合中。
当主机支持文档类型组件时,主机可以定义文档支持的接口的GUID。主机通常还负责创建/删除实际文档,就像只有Excel对象模型可以将工作表添加到工作簿一样,而VBIDE则不能。
您已经谈过工作簿和工作表,因此我将包括两个....
不幸的是,VBIDE隐藏了有关文档类型组件的一些细节,并且在导出模块时故意省略了这些细节,甚至将导出的文档类型模块转换为类模块文本,如此Worksheet
调用Sheet1
,以便无法重新导入为文档类型模块:
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Sheet1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Sub Foo()
End Sub
将上述内容与Sheet1
模块中实际存储的文档模块文本(压缩格式)进行比较:
Attribute VB_Name = "Sheet1"
Attribute VB_Base = "0{00020820-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True
Sub Foo()
End Sub
请注意真实模块文本中存在的3个附加属性:
Attribute VB_Base = "0{00020820-0000-0000-C000-000000000046}"
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True
根据OleViewer,GUID 0 {00020820-0000-0000-C000-000000000046}与CoClass Worksheet
完全匹配:
[
uuid(00020820-0000-0000-C000-000000000046),
helpcontext(0x0002a410)
]
coclass Worksheet {
[default] interface _Worksheet;
[default, source] dispinterface DocEvents;
};
Workbook模块出现相同的行为。这是VBIDE导出的文本:
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "ThisWorkbook"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
来自VBA二进制文件中IStream的原始文本:
Attribute VB_Name = "ThisWorkbook"
Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True
这一次,正如预期的那样,GUID 0{00020819-0000-0000-C000-000000000046}
是一个工作簿CoClass:
[
uuid(00020819-0000-0000-C000-000000000046),
helpcontext(0x000305b8)
]
coclass Workbook {
[default] interface _Workbook;
[default, source] dispinterface WorkbookEvents;
};
以上内容都是有用的,但它并不能解决您的问题,除非您能够处理组件的内存中IStream,我认为您无法做到。如果您可以从上次保存的主机文档版本加载详细信息,那么您可以从基础文档加载详细信息,但我认为您不希望这样,和它最终可能是特定于主机的(考虑Access将VBA存储在表中的方式。)
然而,VBIDE 确实为您提供有关CoClass的线索。 vbComponent的属性集合返回CoClass中存在的完全数量的属性,如果您检查这些属性的名称,参数和类型,您将发现它们完全匹配相应CoClass的成员,直到它们在CoClass定义中出现的顺序。
例如,Worksheet vbComponent的前10个属性是:
Application
Creator
Parent
CodeName
_CodeName
Index
Name
Next
OnDoubleClick
OnSheetActivate
来自CoClass工作表中的dispinterface _Worksheet的相应propget
(和propput
)条目(删除了方法):
[id(0x00000094), propget, helpcontext(0x0002a411)]
Application* Application();
[id(0x00000095), propget, helpcontext(0x0002a412)]
XlCreator Creator();
[id(0x00000096), propget, helpcontext(0x0002a413)]
IDispatch* Parent();
[id(0x0000055d), propget, helpcontext(0x0002a7fc)]
BSTR CodeName();
[id(0x80010000), propget, helpcontext(0x0002a7fd)]
BSTR _CodeName();
[id(0x80010000), propput, helpcontext(0x0002a7fd)]
void _CodeName([in] BSTR rhs);
[id(0x000001e6), propget, helpcontext(0x0002a7fe)]
long Index();
[id(0x0000006e), propget, helpcontext(0x0002a800)]
BSTR Name();
[id(0x0000006e), propput, helpcontext(0x0002a800)]
void Name([in] BSTR rhs);
[id(0x000001f6), propget, helpcontext(0x0002a801)]
IDispatch* Next();
[id(0x00000274), propget, hidden, helpcontext(0x0002a802)]
BSTR OnDoubleClick();
[id(0x00000274), propput, hidden, helpcontext(0x0002a802)]
void OnDoubleClick([in] BSTR rhs);
[id(0x00000407), propget, hidden, helpcontext(0x0002a803)]
BSTR OnSheetActivate();
如果您可以反映主机类型库的CoClasses并散列属性 names (可能只是使用propget名称),那么您可以将哈希值与名称中的哈希值进行比较VBIDE的component.Properties集合。
这是一种获取类型的圆形方式,但如果无法访问IStream,我认为它将成为您唯一的方式。