VBA 中的两个对象何时相同?
When are two objects the same in VBA?
我在 Excel 2010 年使用了功能区,其中包含一个按钮:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="rx_onLoad">
<ribbon>
<tabs>
<tab id="TestTab" label="Test Tab" insertAfterMso="TabHome">
<group id="TestGroup" label="TestGroup">
<button id="TestButton" label="TestButton" size="normal" onAction="OnTestButton" tag="TestButton" imageMso="Coffee" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
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
主动sheet有方法
Public Sub Test()
MsgBox "Test"
End Sub
如果我单击测试按钮,将调用方法 OnTestButton
。
对象 Control.Context.ActiveSheet
和 ActiveSheet
在 IS
运算符下是相同的。当我在 WorkSheet
的界面中使用像 Name
这样的 属性 时,它们的行为相同。
但是,当我调用不在接口中的方法 Test
时,我在 Control.Context.ActiveSheet
上收到运行时错误 458 "Object does not support this property or method" 但在 ActiveSheet
.
上没有
那么,为什么两个引用 Control.Context.ActiveSheet
和 ActiveSheet
在运行时表现不同,而它们应该引用 "same" 对象?
我有理由 (>90%) 确定以下内容是正确的。
先回答题目中的问题:VBA什么时候两个对象相同?
两个对象在VBA中是相同的,当COM说它们相同时,COM说它们是相同的,当你request the IUnknown
interface from both and the pointers come out equal.
现在到有问题的对象。
丝带的Control.Context
无非就是Excel的Application.ActiveWindow
,所以问题就变成了,是ActiveWindow.ActiveSheet
和Application.ActiveSheet
一样。
是的,它们是 - 就 COM 而言。
它们可能没有在内部实现作为单个对象,因为它们的指针彼此相距很远,但是当您向它们请求 IUnknown
时,它们 return相同的指针值。 (您通过声明类型为 IUnknown
的变量并 Set
将对象指向该变量来请求 IUnknown
。)
旁注。
Excel 对于 "true object".[=] 的单个 "actual" "internal" 实例具有多个 "external" "instances" 对象是很自然的74=]
例如您可以创建 Range
对象的多个实例,所有这些实例都是不同的实例 (Is = False
),它们引用实际 sheet 上完全相同的实际范围。因此,对于 "actual thing".
,每个人都只是 "viewport"
我推测 Window
s 和 Sheet
s 正在发生类似的事情(每个实际事物可能有多个 "viewports"),但是 Excel开发人员,为了避免 confusion/to 简化 VBA 编码,决定让 Sheet
s 包装器通过 returning 相同的 IUnknown
来报告它们是同一对象指针。
而对于COM而言,这很好:只要遵循所有COM规则,对象内部实现为多个对象并不重要,因为只要遵循所有COM规则,就有无论如何都无法区分它们。
现在真正的问题是,为什么不能在 ActiveWindow.ActiveSheet
.
上调用 Test()
因为ActiveWindow.ActiveSheet
return是Worksheet
接口,它没有Test()
方法,是。就这么简单。
那为什么Test()
可以在Application.ActiveSheet
上调用呢?
因为 Application.ActiveSheet
没有 return 一个 Worksheet
。它 returns Sheet1
(或任何你的 sheet 被命名)。
Sheet1
是一个动态接口,它继承自 Worksheet
并包含您的 Test()
。它是 Worksheet
.
的超集
您可能想知道,当用户尝试在 Worksheet
上调用 Test()
时,为什么 VBA 不请求更好的 Worksheet
超集。答案是,不应该,也不可以!
VBA 不应该也不需要了解托管它的应用程序的内部实现细节。它无法知道存在一个"better"接口可以从它当前拥有的接口查询。如果它确实怀疑有一个 "better" 接口,它究竟会尝试查询哪个接口,因为每个对象都可以有数百个接口?
VBA 唯一需要执行后期绑定调用的是 IDispatch
接口。
所有 Excel 接口都继承自 IDispatch
。
即ExcelIs IDispatch
.
中的每个class
As Object
类型的变量也表示 As IDispatch
.
将某些内容设置为 Object
变量意味着从该对象中查询 IDispatch
。
ActiveWindow.ActiveSheet
returns IDispatch
是 Worksheet
的一部分。它是有效的,完整的 IDispatch
存储在 IDispatch
类型的变量中,因此 不需要 VBA 请求 "better IDispatch
"。它使用已有的那个,调用失败。
Application.ActiveSheet
returns IDispatch
是 Sheet1
的一部分,一个不同的界面。这次Test()
成功了
从 Worksheet
ActiveWindow.ActiveSheet
returning IDispatch
是否是一个错误是有争议的。
从技术上讲,这不是错误,因为 Worksheet
是 IDispatch
所以该方法是 return 的权利。
然而,可以说 return 对 "better IDispatch
" 是我们在 Excel 中滚动的方式,他们真的应该这样做。
我个人倾向于宣布这是一个小错误。
不过你可以自己申请"better IDispatch
"。
如果你简单地声明另一个 Object
变量并且 Set
你现有的工作 sheet 引用它是行不通的 - VBA 会观察到这两个变量属于同一类型, Object
,并且会直接复制指针而不是尝试查询另一个接口。
要让 VBA 实际请求另一个接口,您需要先查询与 IDispatch
不同的另一个接口,然后从中请求 IDispatch
:
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
我在 Excel 2010 年使用了功能区,其中包含一个按钮:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="rx_onLoad">
<ribbon>
<tabs>
<tab id="TestTab" label="Test Tab" insertAfterMso="TabHome">
<group id="TestGroup" label="TestGroup">
<button id="TestButton" label="TestButton" size="normal" onAction="OnTestButton" tag="TestButton" imageMso="Coffee" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
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
主动sheet有方法
Public Sub Test()
MsgBox "Test"
End Sub
如果我单击测试按钮,将调用方法 OnTestButton
。
对象 Control.Context.ActiveSheet
和 ActiveSheet
在 IS
运算符下是相同的。当我在 WorkSheet
的界面中使用像 Name
这样的 属性 时,它们的行为相同。
但是,当我调用不在接口中的方法 Test
时,我在 Control.Context.ActiveSheet
上收到运行时错误 458 "Object does not support this property or method" 但在 ActiveSheet
.
那么,为什么两个引用 Control.Context.ActiveSheet
和 ActiveSheet
在运行时表现不同,而它们应该引用 "same" 对象?
我有理由 (>90%) 确定以下内容是正确的。
先回答题目中的问题:VBA什么时候两个对象相同?
两个对象在VBA中是相同的,当COM说它们相同时,COM说它们是相同的,当你request the IUnknown
interface from both and the pointers come out equal.
现在到有问题的对象。
丝带的Control.Context
无非就是Excel的Application.ActiveWindow
,所以问题就变成了,是ActiveWindow.ActiveSheet
和Application.ActiveSheet
一样。
是的,它们是 - 就 COM 而言。
它们可能没有在内部实现作为单个对象,因为它们的指针彼此相距很远,但是当您向它们请求 IUnknown
时,它们 return相同的指针值。 (您通过声明类型为 IUnknown
的变量并 Set
将对象指向该变量来请求 IUnknown
。)
旁注。
Excel 对于 "true object".[=] 的单个 "actual" "internal" 实例具有多个 "external" "instances" 对象是很自然的74=]
例如您可以创建 Range
对象的多个实例,所有这些实例都是不同的实例 (Is = False
),它们引用实际 sheet 上完全相同的实际范围。因此,对于 "actual thing".
我推测 Window
s 和 Sheet
s 正在发生类似的事情(每个实际事物可能有多个 "viewports"),但是 Excel开发人员,为了避免 confusion/to 简化 VBA 编码,决定让 Sheet
s 包装器通过 returning 相同的 IUnknown
来报告它们是同一对象指针。
而对于COM而言,这很好:只要遵循所有COM规则,对象内部实现为多个对象并不重要,因为只要遵循所有COM规则,就有无论如何都无法区分它们。
现在真正的问题是,为什么不能在 ActiveWindow.ActiveSheet
.
Test()
因为ActiveWindow.ActiveSheet
return是Worksheet
接口,它没有Test()
方法,是
那为什么Test()
可以在Application.ActiveSheet
上调用呢?
因为 Application.ActiveSheet
没有 return 一个 Worksheet
。它 returns Sheet1
(或任何你的 sheet 被命名)。
Sheet1
是一个动态接口,它继承自 Worksheet
并包含您的 Test()
。它是 Worksheet
.
您可能想知道,当用户尝试在 Worksheet
上调用 Test()
时,为什么 VBA 不请求更好的 Worksheet
超集。答案是,不应该,也不可以!
VBA 不应该也不需要了解托管它的应用程序的内部实现细节。它无法知道存在一个"better"接口可以从它当前拥有的接口查询。如果它确实怀疑有一个 "better" 接口,它究竟会尝试查询哪个接口,因为每个对象都可以有数百个接口?
VBA 唯一需要执行后期绑定调用的是 IDispatch
接口。
所有 Excel 接口都继承自 IDispatch
。
即ExcelIs IDispatch
.
As Object
类型的变量也表示 As IDispatch
.
将某些内容设置为 Object
变量意味着从该对象中查询 IDispatch
。
ActiveWindow.ActiveSheet
returns IDispatch
是 Worksheet
的一部分。它是有效的,完整的 IDispatch
存储在 IDispatch
类型的变量中,因此 不需要 VBA 请求 "better IDispatch
"。它使用已有的那个,调用失败。
Application.ActiveSheet
returns IDispatch
是 Sheet1
的一部分,一个不同的界面。这次Test()
成功了
从 Worksheet
ActiveWindow.ActiveSheet
returning IDispatch
是否是一个错误是有争议的。
从技术上讲,这不是错误,因为 Worksheet
是 IDispatch
所以该方法是 return 的权利。
然而,可以说 return 对 "better IDispatch
" 是我们在 Excel 中滚动的方式,他们真的应该这样做。
我个人倾向于宣布这是一个小错误。
不过你可以自己申请"better IDispatch
"。
如果你简单地声明另一个 Object
变量并且 Set
你现有的工作 sheet 引用它是行不通的 - VBA 会观察到这两个变量属于同一类型, Object
,并且会直接复制指针而不是尝试查询另一个接口。
要让 VBA 实际请求另一个接口,您需要先查询与 IDispatch
不同的另一个接口,然后从中请求 IDispatch
:
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