我可以转换为任何对象的默认接口吗?

问题描述 投票:0回答:1

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

但我更喜欢检查函数签名中的类型,然后在需要时向上转换回默认接口的工作流程

excel vba interface reference
1个回答
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

在第二行,可能会发生 2 件事:

  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 接口知道默认接口,这就是返回的内容

编辑2024年5月8日

假设

Ole Automation
引用未打开,那么显然
IUnknown
会引发编译时错误。

替代方法是使用任何其他自定义界面。例如,

VBEGlobal
始终作为
VBA
库本身的一部分可用:

Public Function GetDefaultInterface(ByVal obj As Object) As Object
    Dim g As VBEGlobal
    MemLongPtr(VarPtr(g)) = ObjPtr(obj)
    Set GetDefaultInterface = g
    MemLongPtr(VarPtr(g)) = NULL_PTR
End Function

其中

MemLongPtr
NULL_PTR
是 [LibMemory] 的一部分https://github.com/cristianbuse/VBA-MemoryTools)

在上面的函数中,以与前面示例相同的方式对

QueryInterface
进行
IDispatch
调用。

© www.soinside.com 2019 - 2024. All rights reserved.