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?

允我心安 提交于 2019-12-19 07:39:34

问题


Question summary

When I use VBA's VarType function, passing it an instance of the Object class available in a mscorlib.dll library reference (a .NET library reference), the value returned is 8.

According to VBA documentation here, this means the object is a string. This seems ridiculous.

My question is why is the VarType function returning a string-type value for instances of this Object class from the .NET-library VBA reference? Is it a bug?

Background information

I suspect that the fact that VBA's VarType function is saying a certain COM object is a string, may be why I'm having problems using the DispCallFunc function on certain methods of certain COM objects. The COM objects are COM versions of .NET objects, made available through the .NET framework.

I'm using the mscorlib.dll VBA reference to get early-binding functionality for these objects. The reference refers to version 4.0.30319 of the .NET framework. On my computer, the type library for the reference is stored at:

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

It is looking like when the object method specifies that an argument or return value be of type System.Object, DispCallFunc fails to work for me. Anyway, that is a separate problem that is circumstantial to the problem this question is dedicated to.


At the time of writing, I'm running the latest version of Excel (Version 1903, Build 11425.20202). My operating system is Windows 8.1.

When I use the VarType function with instances of other classes from the mscorlib.dll library, I sometimes get return values of 9 & 13 (VbVarType.vbObject & VbVarType.vbDataObject constants) which seems correct.

I researched on the internet to see whether anyone else had encountered the problem but couldn't find anything.

Code that can be used to reproduce problem

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

I expected VarType(CVar(o)) to return 9, 13 or some other appropriate integer. Instead, it returned 8 which does not seem appropriate at all (8 represents strings.)


回答1:


If you open the typeLib C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb with OleView for example, and navigate to the _Object interface (not the first _Object one, the dispinterface, but the second one) you'll see this:

So, the .NET method Object.ToString() is declared to COM/Automation clients as

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

Which means it's seen to COM clients that understand this "syntactic sugar" (VB/VBA/VBScript/JScript/.NET, etc.) as a Property (propget + out + retval) named ToString that returns a String (BSTR).

Now, id(0) means it's the default property, because 0 represents DISPID_VALUE, a special well-known id.

And lastly, VB/VBA VarType's documentation states this:

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

(which I always found a pretty strange design decision...)




回答2:


Looking at the reference source for object.cs, I'm not seeing any [DispId] attributes, but assuming the first member gets marshaled with [DispId(0)], that would make the ToString method the COM type's default member.

That's the only explanation I have for Debug.Print o outputting System.Object, rather than blowing up with error 438 as it normally would without a default member.

So the problem isn't so much with .NET/COM interop, rather it's about dealing with getting metadata out of an object that has a default member: you would have the exact same problem with any COM object that has a String default member:

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

I can't think of a way off the top of my head to make VarType work with these. On the other hand, a TypeOf...Is check works fine:

?TypeOf Application Is Object
True

Hence:

Debug.Print TypeOf o Is Object ' True



回答3:


I think a part of the problem is that VBA evaluates expressions in certain cases (maybe someone can add more information on when/why this happens). So, when you make the call to VarType(o), the o variable is actually being converted to a string representation, and the type is taken.

As an example, if you write Debug.Print o, the output will be System.Object. If you write Debug.Print x on a different, concrete object, the system will possibly throw an error.

Try the following syntax:

Debug.Print VarType(o.GetType)

In my case, that returns the value 13.



来源:https://stackoverflow.com/questions/55659056/why-is-vbas-vartype-function-saying-this-com-object-is-a-string-object-is-ins

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!