VBA中两个对象何时相同?

时间:2016-04-08 09:10:41

标签: vba identity

我在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

活动表格有方法

Public Sub Test()
    MsgBox "Test"
End Sub

如果单击测试按钮,则调用方法OnTestButton。 对象Control.Context.ActiveSheetActiveSheetIS - 运算符下相同。当我使用Name之类的属性时,它位于WorkSheet的界面中,它们的行为相同。 但是,当我调用方法Test时,不在界面中,我在Control.Context.ActiveSheet上得到运行时错误458“对象不支持此属性或方法”,但ActiveSheet上没有。

那么为什么这两个引用Control.Context.ActiveSheetActiveSheet在运行时它们应该引用“相同”对象时表现不同呢?

1 个答案:

答案 0 :(得分:4)

我合理地(> 90%)确定以下内容是真的。

首先回答标题中的问题:VBA中两个对象何时相同?

当COM表示它们相同时,VBA中的两个对象是相同的,而COM表示当request the IUnknown interface from both and the pointers come out equal时它们是相同的。

现在对相关对象。

功能区的Control.Context只不过是Excel Application.ActiveWindow,因此问题变为 ActiveWindow.ActiveSheetApplication.ActiveSheet相同。

是的,他们 - 就COM而言 它们可能没有在内部实现作为单个对象,因为它们的指针彼此相距很远,但是当您从它们请求IUnknown时,它们会返回相同的指针值。 (您通过声明类型IUnknownIUnknown对象的变量来请求Set。)

附注。
Excel有多个&#34;外部&#34; &#34;实例&#34;对于单个&#34;实际&#34; &#34;内部&#34; &#34;真实对象的实例&#34;。

E.g。您可以创建Range对象的多个实例,所有实例都是不同的实例(Is = False),这些实例引用实际工作表上完全相同的实际范围。因此,每一个都只是一个视频&#34;对于&#34;实际的事情&#34;。

我推测WindowSheet s也会发生类似的事情(可能有多个&#34;视口和#34;对于每个实际的东西),但Excel开发人员,为了避免混淆/简化VBA编码,决定让Sheet包装器通过返回相同的IUnknown指针报告它们是同一个对象。

就COM而言,这是好的:只要遵循所有COM规则,如果对象在内部实现为多个对象并不重要,因为只要遵循所有COM规则,就没有无论如何,告诉他们分开的方式。

现在是实际问题,为什么Test() 无法调用ActiveWindow.ActiveSheet

因为ActiveWindow.ActiveSheet会返回Worksheet接口,该接口没有Test()方法且为non-extensible。就这么简单。

那么为什么Test()可以调用Application.ActiveSheet? 因为Application.ActiveSheet未返回Worksheet。它返回Sheet1(或者您的工作表命名)。

Sheet1是一个动态界面,继承自Worksheet并包含您的Test()。它是Worksheet的超集。

您可能想知道为什么当用户尝试在Worksheet上呼叫Test()时,VBA不会请求更好的Worksheet超集。答案是,既不应该,也不能!

VBA不应该也不需要了解托管它的应用程序的内部实现细节。它不可能知道存在更好的&#34;可以从当前界面查询的界面。如果它确实怀疑有一个更好的&#34;接口,考虑到每个对象可以有数百个接口,它究竟会尝试查询哪一个?

VBA唯一需要执行后期绑定调用的是IDispatch接口 所有Excel界面都继承自IDispatch。 也就是说,Excel中的每个类Is IDispatch

As Object类型的变量也意味着As IDispatch 将某些内容设置为Object变量意味着从该对象中查询IDispatch

ActiveWindow.ActiveSheet会返回IDispatch的{​​{1}}。它是有效的,完整的Worksheet存储在IDispatch类型的变量中,因此不需要VBA要求更好IDispatch&# 34; 即可。它使用已有的那个,并且调用失败。

IDispatch返回Application.ActiveSheet,它是IDispatch的一部分,是一个不同的界面。这次Sheet1成功。

Test()ActiveWindow.ActiveSheet返回IDispatch是否是一个错误,这是否值得商榷。
从技术上讲,它不是一个错误,因为Worksheet Worksheet所以该方法有权返回。
然而,可以说回归更好IDispatch&#34;我们是如何在Excel中滚动的,他们真的应该这样做 我个人倾向于宣称这是一个小错误。

但你可以要求更好IDispatch&#34;你自己。
如果您只是声明另一个IDispatch变量并且{/ 1}}您现有的工作表引用,那么它将无法工作 - VBA将观察到两个变量的类型相同,Object和将直接复制指针而不是试图查询另一个接口。

要让VBA实际要求另一个界面,首先需要查询与Set不同的其他界面,然后从中请求Object

IDispatch