Why when trying to assign to a variant, an array for some non-IDispatch-exposing object type, is the array pointer assigned instead?
Asked Answered
C

0

6

Summary

After declaring an array for some object type where the type doesn't use an interface that derives from COM's IDispatch interface (& so isn't properly set-up to enable [OLE] Automation's late-binding technology), trying to assign the array to a Variant variable doesn't work according to my expectations.

Specifically, what appears to happen is that an integral pointer to the array is assigned to the Variant variable, instead of a copy of the array.

Can anyone explain such behaviour? Is it standard behaviour of the VBA 7.1 language? [EDIT-A1] Is it that VBA's Variant type can't handle such arrays? [EDIT-A2]


[EDIT-A1] - Currently (4/6/19), it's looking like it's a bug.
[EDIT-A2] - No, as the internal procedure parameters for ByRef Variant arguments can properly point to such arrays, if such arrays are passed as the values for such arguments.



Background

More details on what appears to be happening

After inspecting various memory addresses & pointers, what appears to be happening is that when assigning such arrays to Variant variables, the Long (or LongPtr) pointers to the 'VB safe array pointers' of the arrays, are instead being assigned. This is contrary to the array-copying behaviour documented here in the VBA language specification as being expected behaviour. I deduced such pointer assignment was taking place after reviewing VB6 documentation on VB arrays & their memory layout, stored here.

My research into whether issue has been previously documented

I've searched the internet, the Office VBA Reference documentation, & the VBA language specification to see whether this problem has been documented by anyone else. The only place I've found where this problem may have already been partly documented is in a Stack Overflow answer posted here. Unfortunately, the post doesn't say much about the problem.

Specific array types that I have tested this issue with

I have experienced this issue with arrays of the following object types that use interfaces not deriving from IDispatch:

  • stdole.IUnknown *
  • stdole.IFont
  • mscoree.CorRuntimeHost
  • mscorlib.AppDomain
  • mscorlib.Type

* From OLE Automation COM type library in stdole2.tlb.
† From COM type library for the .NET framework v4.0.30319.

More on what an object type that uses an interface not deriving from IDispatch, is

Such object types are vbDataObject types. Unfortunately, the current VBA documentation on the vbDataObject constant is inaccurate. I'm in the process of updating the documentation, & you can see my updated definition for the constant here.

[EDIT - COM specification link removed as it is no longer relevant.]


Regarding arrays of other object types

Arrays of vbObject types (COM object types that support [OLE] Automation late-binding technology) appear to be assigned to Variant variables in a straight-forward & expected manner - array is copied and directly assigned.

VBE used to crash when inspecting such arrays using a VBE watch

A potentially related issue is to do with a bug that used to occur when a VBE watch expression was placed on an array of a type that used an interface not deriving from IDispatch. The bug used to cause the VBE to unexpectedly crash. This bug appears to have been fixed by Microsoft within the last month.

Software versions

  Software               | Version      
-------------------------+---------------
VBA | 7.1
-------------------------+---------------
Microsoft Excel | 2016 §
-------------------------+---------------
Microsoft Windows (OS) | 8.1

‡ Full text for version is "Retail 7.1.1088"—the latest version.
§ More specifically, the latest version & build (released on 3rd June 2019, version 1905, build 11629.20214), & also earlier build numbers 11629.20196 & 11601.20204 (first was released on 14th May 2019).
ver.exe program prints "Microsoft Windows [Version 6.3.9600]".

Code

Reprex:

    Dim MyArray() As stdole.IUnknown ' Type from OLE Automation 
                                     ' COM type library in stdole2.tlb.
    Dim MyVariant As Variant
    MyVariant = MyArray

I expected MyVariant to store a copy of the MyArray array.

Instead, in the 32-bit version of VBA, MyVariant stores a Long value that appears to be a pointer to MyArray.

Debugging issue

Click here for VBA code to assist in debugging issue.

Corrie answered 15/5, 2019 at 18:40 Comment(9)
Interestingly, if you pass MyArray into a routine that accepts ByRef v As Variant, the TypeName(v) inside it is correctly Unknown(). So you can have public function CorrectlyAssign(ByRef v As Variant) As Variant : CorrectlyAssign = v : End Function and do MyVariant = CorrentlyAssign(MyArray).Ophthalmoscopy
Yet another array-to-pointer bug in VBA, I am being kept amused. (Other examples are https://mcmap.net/q/392194/-vba-what-is-causing-this-string-argument-passed-to-paramarray-to-get-changed-to-a-number-that-looks-suspiciously-like-a-pointer/11683 and https://mcmap.net/q/244843/-how-do-i-determine-if-an-array-is-initialized-in-vb6).Ophthalmoscopy
Hello @Ophthalmoscopy - thanks for these comments. It's looking as though it is a VBA bug that occurs in type conversions from vbDataObject-object arrays to Variant data whenever array copying is involved. In the case of the ByRef type conversion, no array copying is involved, so the type conversion appears to be working as expected - such behaviour is still odd because it differs from the ByVal type conversion, with the ByVal behaviour being even odder.Corrie
Have now sent feedback using Microsoft Excel Feedback feature, to Microsoft regarding this issue. Have asked them to confirm whether it is a bug.Corrie
Good luck with that (honestly), although I personally haven't had any success with my bug reports.Ophthalmoscopy
You're playing a dangerous game with the VBA type system. It has no support whatsoever for IUnknown interfaces, it expects all of them to be IDispatch. It probably suffers an internal heart attack while trying to copy the array elements on a failing QueryInterface call and punts for returning the array pointer instead. This isn't going to get fixed, you need to declare the array As Object.Vagrant
Hello @HansPassant. Thanks for your feedback. I don't think you are quite right about VBA not providing any support for IUnknown interfaces. Some of the COM types for the .NET framework do not support IDispatch but still work pretty well. See here for someone else's opinion on whether pre-.NET VB supports IUnknown interfaces.Corrie
Hmya, "Of course, you can't really "work" with it" is the relevant phrase.Vagrant
I don't know why it happens but you can use this or this to make the copyRefractory

© 2022 - 2024 — McMap. All rights reserved.