VBA中的两个对象何时相同?
我在Excel 2010中使用了一个功能区,其中包含一个按钮:VBA中的两个对象何时相同?,vba,identity,Vba,Identity,我在Excel 2010中使用了一个功能区,其中包含一个按钮: OnTestButton方法在一个模块中实现 Sub OnTestButton(Control As IRibbonControl) Dim Ws As Object Set Ws = Control.Context.ActiveSheet MsgBox ActiveSheet Is Ws ' Shows True Debug.Print ActiveSheet.Name 'OK D
OnTestButton方法在一个模块中实现
Sub OnTestButton(Control As IRibbonControl)
Dim Ws As Object
Set Ws = Control.Context.ActiveSheet
MsgBox ActiveSheet Is Ws ' Shows True
Debug.Print ActiveSheet.Name 'OK
Debug.Print Ws.Name ' OK
ActiveSheet.Test ' OK
Debug.Print Ws.Test ' Runtime Error
End Sub
活动工作表有一个方法
Public Sub Test()
MsgBox "Test"
End Sub
如果单击测试按钮,则调用方法OnTestButton
。
对象Control.Context.ActiveSheet
和ActiveSheet
在IS
-操作符下是相同的。当我在工作表
的界面中使用类似名称
的属性时,它们的行为是相同的。
但是,当我调用不在接口中的方法Test
时,我在Control.Context.ActiveSheet
上得到运行时错误458“Object不支持此属性或方法”,但在ActiveSheet
上没有
那么,为什么这两个引用控制.Context.ActiveSheet
和ActiveSheet
在运行时应该引用“相同”的对象时表现不同呢?我有理由(>90%)确信下面的说法是正确的
首先回答标题中的问题:VBA中的两个对象何时相同
在VBA中,当COM表示两个对象相同时,这两个对象是相同的;而当您使用VBA时,COM表示两个对象是相同的
现在来谈谈这些问题
功能区的控件.Context
只不过是Excel的应用程序.ActiveWindow
,所以问题变成了,是ActiveWindow.ActiveSheet
还是Application.ActiveSheet
一样。
是的,他们是——就COM而言。它们可能不是作为单个对象在内部实现的,因为它们的指针彼此相距很远,但是当您向它们请求
IUnknown
时,它们返回相同的指针值。(您可以通过声明类型为IUnknown
的变量来请求IUnknown
,并将对象设置为该类型。)
旁注。
对于Excel来说,对于一个“真实对象”的单个“实际”“内部”实例,有多个对象的“外部”“实例”是很自然的
例如,您可以创建范围
对象的多个实例,所有这些实例都将是不同的实例(Is=False
),引用实际工作表上完全相同的实际范围。因此,每一个都只是“真实事物”的“视口”
我推测类似的事情也发生在Window
s和Sheet
s上(每个实际的东西可以有多个“视口”),但是Excel开发人员为了避免混淆/简化VBA编码,决定让Sheet
s包装器通过返回相同的IUnknown
指针来报告它们是相同的对象
就COM而言,这很好:只要遵循所有COM规则,对象是否在内部实现为多个对象并不重要,因为只要遵循所有COM规则,就没有办法区分它们
现在实际的问题是,为什么不能在ActiveWindow.ActiveSheet
上调用Test()
因为ActiveWindow.ActiveSheet
返回工作表
接口,该接口没有Test()
方法,并且是。就这么简单
那么为什么可以在Application.ActiveSheet
上调用Test()
?
因为Application.ActiveSheet
不返回工作表
。它返回Sheet1
(或任何工作表名称)
Sheet1
是一个动态界面,它继承自工作表
,并包含您的Test()
。它是工作表
的超集
您可能想知道,当用户试图在工作表上调用Test()
时,为什么VBA不请求更好的工作表超集。答案是,它既不应该,也不能
VBA不应该也不需要了解托管它的应用程序的内部实现细节。它无法知道存在一个“更好”的接口,可以从当前的接口查询该接口。如果它确实怀疑有一个“更好”的接口,那么考虑到每个对象可以有数百个接口,它会尝试查询哪个接口
VBA执行后期绑定调用所需的唯一东西是IDispatch
接口。
所有Excel界面都继承自IDispatch
。
也就是说,Excel中的每个类都是IDispatch
类型为As Object
的变量也表示As IDispatch
将某物设置为对象
变量意味着从该对象中查询IDispatch
ActiveWindow.ActiveSheet
返回IDispatch
,它是工作表的一部分。它是有效的、完整的IDispatch
存储在IDispatch
类型的变量中,因此VBA无需要求“更好的IDispatch
”。它使用它已有的一个,调用失败
Application.ActiveSheet
返回IDispatch
,它是另一个界面Sheet1
的一部分。这次Test()
成功
从工作表返回IDispatch
的ActiveWindow.ActiveSheet
是否是一个bug还有争议。
从技术上讲,这不是一个bug,因为工作表
是IDispatch
,因此该方法有权返回该值。
但是可以说,返回“betterIDispatch
”是我们在Excel中滚动的方式,他们真的应该这样做。
我个人倾向于宣布它是一个小错误
但你可以要求
Dim BadIDispatch As Object
Set BadIDispatch = Control.Context.ActiveSheet 'ActiveWindow.ActiveSheet
Dim Ws As Worksheet
Set Ws = BadIDispatch 'Querying another interface
Dim WsAsObject As Object
Set WsAsObject = Ws 'Querying IDispatch - this time going to get a good one