我可以投射到任何对象的默认界面吗?

时间:2019-08-17 12:35:36

标签: excel vba interface reference

我了解在VBA中,所有类都公开一个默认接口(这只是类模块的名称)。您还可以使它们Implement成为另一个自定义界面;为该类提供一些属性,这些属性从自定义界面的角度是可见的,但是从默认界面的角度是不可见的。

我有一个方法,它期望实现特定接口的类

Public Sub doStuff(ByVal item As ICustomInterface)

称呼为

Dim a As New Class1 'Implements ICustomInterface
Dim b As New Class2 'Implements ICustomInterface too

doStuff a
doStuff b
doStuff New Collection 'raises "runtime error 13 - type mismatch" as Collection doesn't implement ICustomInterface

如果我正确理解,则在向该方法提供对象的实例时,通过传递对该对象默认接口的引用,VBA会查询该对象实例以生成对该对象的ICustomInterface的引用,并将新引用存储在item变量中。我认为这个过程称为向下转换。

我的问题是doStuff调用了一种方法,要求传递商品的默认界面,不是自定义界面。


为了演示,我们可以使用ObjPtr来标识所指向的接口:

Dim implementation As Object
Dim defaultCast As Class1 'implements ICustomInterface
Dim downCast As ICustomInterface

Set implementation = New Class1 'or Class2 - store reference to default interface in variable

'1) Check if implementation indeed points to default interface
Set defaultCast = implementation
Debug.Assert ObjPtr(defaultCast) = ObjPtr(implementation) 'fine

'2) Check if down-casting gives different interface
Set downCast = implementation
Debug.Assert ObjPtr(downCast) <> ObjPtr(implementation) 'fine

'4) Check if casting from ICustomInterface to Object reverts to default interface
Dim objectUpCast As Object
Set objectUpCast = downCast
Debug.Assert ObjPtr(objectUpCast) = ObjPtr(implementation) 'fails :(
Debug.Assert ObjPtr(objectUpCast) = ObjPtr(downCast) 'succeeds - not what I want

'3) Check if casting from ICustomInterface to Class1 reverts to Class1's default interface
Dim strictUpCast As Class1
Set strictUpCast = downCast
Debug.Assert ObjPtr(strictUpCast) = ObjPtr(implementation) 'fine - but won't work if I Set implementation = New Class2
'/some other implementation of the ICustomInterface

第三个选项;使用自定义界面并恢复为默认界面是我想要的

为什么?

我希望在函数签名中输入安全性。我可以显式使用自定义界面-这是我当前的解决方法

Public Sub doStuff(ByVal item As Object) 'receive default interface - or at least whatever interface is provided

    Dim downCast As ICustomInterface
    Set downCast = item

    'work with downCast as necessary
    '... later pass default interface "item" to other sub

End Sub

但是我更喜欢在我的函数签名中检查类型,然后在需要时向上投射回默认界面的工作流程

1 个答案:

答案 0 :(得分:1)

首先强制转换为IUnknown:

    Dim objectUpCast As Object
    Dim iUnk As IUnknown
    Set iUnk = downCast
    Set objectUpCast = iUnk

修改

所有VB接口都是双重接口,这意味着所有接口均来自IDispatch,而IDispatch又来自IUnknown。

请注意,Object数据类型代表IDispatch接口。

在您的示例中:

Dim strictUpCast As Class1
Set strictUpCast = downCast

第二行调用QueryInterface,按预期方式请求Class1接口。

在另一个示例中:

Dim objectUpCast As Object
Set objectUpCast = downCast

在第二行中会发生两种情况:

  1. 如果变量(downCast)指向双接口(从IDispatch派生的接口),则VB只会增加引用计数(因此返回与downCast相同的接口)
  2. 如果变量指向自定义接口(源自IUnknown),则VB调用QueryInterface并询问IDispatch接口

强制转换为IUnknown接口(OLE自动化库的一部分)时:

Dim iUnk As IUnknown
Set iUnk = downCast

iUnk变量指向自定义界面(仅来自IUnknown)。

这将达到:

Dim objectUpCast As Object
Set objectUpCast = iUnk

在第二行中,VB调用QueryInterface并请求IDispatch接口,因为变量(右侧)指向自定义接口(IUnknown本身,它不是从IDispatch派生的)。 IDispatch接口知道默认接口,这就是返回的