为什么 VBA 的 VarType 函数说这个 COM 对象是一个字符串? (对象是 .NET System.Object class 的 COM 版本的实例。)这是一个错误吗?

Why is VBA's VarType function saying this COM object is a string? (Object is instance of COM version of .NET's System.Object class.) Is it a bug?

问题总结

当我使用 VBA 的 VarType function, passing it an instance of the Object class available in a mscorlib.dll library reference (a .NET 库引用时,returned 的值是 8

根据 VBA 文档 here,这意味着该对象是一个字符串。这看起来很可笑。

我的问题是为什么 VarType 函数 return 从 .NET 库 VBA参考?这是一个错误吗?

背景资料

我怀疑 VBA 的 VarType 函数是在说某个 COM 对象是一个字符串,这可能就是我在使用 DispCallFunc 函数时遇到问题的原因在某些 COM 对象的某些方法上。 COM 对象是 .NET 对象的 COM 版本,可通过 .NET 框架获得。

我正在使用 mscorlib.dll VBA 引用来获取这些对象的早期绑定功能。该参考指的是 .NET 框架的 4.0.30319 版。在我的电脑上,引用的类型库存储在:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb.

当对象方法指定参数或 return 值的类型为 System.Object 时,DispCallFunc 似乎对我不起作用。无论如何,这是一个单独的问题,是这个问题专门针对的问题的间接问题。


在撰写本文时,我是 运行 Excel 的最新版本(版本 1903,内部版本 11425.20202)。我的操作系统是 Windows 8.1.


当我将 VarType 函数与 mscorlib.dll 库中的其他 class 实例一起使用时,我有时会得到 9 的 return 值 & 13VbVarType.vbObject & VbVarType.vbDataObject 常量)这似乎是正确的。

我在互联网上进行了搜索,看看是否有其他人遇到过该问题,但一无所获。

可用于重现问题的代码

Dim o As mscorlib.Object
Set o = New mscorlib.Object
Debug.Print "TypeName(o) = " & TypeName(o) ' TypeName function seems to work correctly.
Debug.Print "o.Equals(o) = " & o.Equals(o) ' System.Object.Equals method is working.

Debug.Print "VarType(CVar(o)) = " & VarType(CVar(o)) ' IMPORTANT LINE
' VBA VarType function says o is string (type 8) but it isn't?!

Debug.Print "VbVarType.vbString = " & VbVarType.vbString

我期望 VarType(CVar(o)) 到 return 913 或其他一些适当的整数。相反,它 returned 8 似乎根本不合适(8 代表字符串。)

我认为部分问题是 VBA 在某些情况下计算表达式(也许有人可以添加更多关于 when/why 这种情况的信息)。因此,当您调用 VarType(o) 时,o 变量实际上被转换为字符串表示形式,并且采用了类型。

例如,如果您写 Debug.Print o,输出将是 System.Object。如果您在不同的具体对象上写 Debug.Print x,系统可能会抛出错误。

尝试以下语法:

Debug.Print VarType(o.GetType)

在我的例子中,returns 值 13。

查看 object.csreference source,我没有看到任何 [DispId] 属性,但假设第一个成员被 [DispId(0)] 编组,这将使ToString 方法 COM 类型的 默认成员 .

这是我对 Debug.Print o 输出 System.Object 的唯一解释,而不是像通常没有默认成员那样出现错误 438。

所以问题不在于 .NET/COM 互操作,而在于处理从具有默认成员的对象中获取元数据:您将遇到与任何 COM 对象完全相同的问题具有 String 个默认成员:

?VarType(Application), VarType(Application.Name)
 8             8 

我想不出让 VarType 使用这些的方法。另一方面,TypeOf...Is 检查工作正常:

?TypeOf Application Is Object
True

因此:

Debug.Print TypeOf o Is Object ' True

例如,如果您使用 OleView 打开 typeLib C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb,并导航至 _Object 界面(不是第一个 _Object 界面,调度界面,而是第二个界面),您会看到这个:

因此,.NET 方法 Object.ToString() 向 COM/Automation 客户端声明为

[id(00000000), propget, custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
HRESULT ToString([out, retval] BSTR* pRetVal);

这意味着 COM 客户端会将此 "syntactic sugar"(VB/VBA/VBScript/JScript/.NET 等)理解为 属性 (propget + out + retval) named ToString that returns a String (BSTR).

现在,id(0)表示它是默认属性,因为0代表DISPID_VALUE,一个特殊的众所周知的id。

最后,VB/VBA VarType's documentation 指出:

If an object has a default property, VarType(object) returns the type of the object's default property.

(我总是发现一个非常奇怪的设计决定...)