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.
MyArray
into a routine that acceptsByRef v As Variant
, theTypeName(v)
inside it is correctlyUnknown()
. So you can havepublic function CorrectlyAssign(ByRef v As Variant) As Variant : CorrectlyAssign = v : End Function
and doMyVariant = CorrentlyAssign(MyArray)
. – OphthalmoscopyvbDataObject
-object arrays toVariant
data whenever array copying is involved. In the case of theByRef
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 theByVal
type conversion, with theByVal
behaviour being even odder. – CorrieIUnknown
interfaces. Some of the COM types for the .NET framework do not supportIDispatch
but still work pretty well. See here for someone else's opinion on whether pre-.NET VB supportsIUnknown
interfaces. – Corrie