Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

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.)

like image 440
Mark Fernandes Avatar asked Apr 12 '19 20:04

Mark Fernandes


People also ask

What is VarType?

VarType(varname) The required varname argument is a Variant containing any variable except a variable of a user-defined type.

How do I find data type in Excel VBA?

Excel VBA VarType Function. VBA VARTYPE means “Variable Type.” This function helps us to identify the data type assigned to the specific variable. Simply put, it finds what kind of value is stored or assigned to the variable.

Which VBA component is used to modify an object?

You use the CodeModule object to modify (add, delete, or edit) the code associated with a component. Each component is associated with one CodeModule object.


2 Answers

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:

enter image description here

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...)

like image 74
Simon Mourier Avatar answered Oct 23 '22 15:10

Simon Mourier


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
like image 29
Mathieu Guindon Avatar answered Oct 23 '22 13:10

Mathieu Guindon