I realized that my original answer can be simplified - rather than having the VARIANT and SAFEARRAY structures defined as VBA Types, all that is needed is a few CopyMemory
s to get the pointers and finally the Integer result.
UPDATE: This version should work on both 32 bit and 64 bit systems (original preserved below the break for posterity):
Option Explicit
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Integer)
Public Function GetDims(VarSafeArray As Variant) As Integer
Dim variantType As Integer
Dim pointer As LongPtr
Dim arrayDims As Integer
CopyMemory VarPtr(variantType), VarPtr(VarSafeArray), 2& 'the first 2 bytes of the VARIANT structure contain the type
If (variantType And &H2000) > 0 Then 'Array (&H2000)
'If the Variant contains an array or ByRef array, a pointer for the SAFEARRAY or array ByRef variant is located at VarPtr(VarSafeArray) + 8
CopyMemory VarPtr(pointer), VarPtr(VarSafeArray) + 8, LenB(pointer)
'If the array is ByRef, there is an additional layer of indirection through another Variant (this is what allows ByRef calls to modify the calling scope).
'Thus it must be dereferenced to get the SAFEARRAY structure
If (variantType And &H4000) > 0 Then 'ByRef (&H4000)
'dereference the pointer to pointer to get the actual pointer to the SAFEARRAY
CopyMemory VarPtr(pointer), pointer, LenB(pointer)
End If
'The pointer will be 0 if the array hasn't been initialized
If Not pointer = 0 Then
'If it HAS been initialized, we can pull the number of dimensions directly from the pointer, since it's the first member in the SAFEARRAY struct
CopyMemory VarPtr(arrayDims), pointer, 2&
GetDims = arrayDims
Else
GetDims = 0 'Array not initialized
End If
Else
GetDims = 0 'It's not an array... Type mismatch maybe?
End If
End Function
Here is the simplest complete GetDims that checks the dimensions directly through the variables in memory:
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Integer)
Public Function GetDims(VarSafeArray As Variant) As Integer
Dim variantType As Integer
Dim pointer As Long
Dim arrayDims As Integer
CopyMemory VarPtr(variantType), VarPtr(VarSafeArray), 2& 'the first 2 bytes of the VARIANT structure contain the type
If (variantType And &H2000) > 0 Then 'Array (&H2000)
'If the Variant contains an array or ByRef array, a pointer for the SAFEARRAY or array ByRef variant is located at VarPtr(VarSafeArray) + 8
CopyMemory VarPtr(pointer), VarPtr(VarSafeArray) + 8, 4&
'If the array is ByRef, there is an additional layer of indirection through another Variant (this is what allows ByRef calls to modify the calling scope).
'Thus it must be dereferenced to get the SAFEARRAY structure
If (variantType And &H4000) > 0 Then 'ByRef (&H4000)
'dereference the pointer to pointer to get the actual pointer to the SAFEARRAY
CopyMemory VarPtr(pointer), pointer, 4&
End If
'The pointer will be 0 if the array hasn't been initialized
If Not pointer = 0 Then
'If it HAS been initialized, we can pull the number of dimensions directly from the pointer, since it's the first member in the SAFEARRAY struct
CopyMemory VarPtr(arrayDims), pointer, 2&
GetDims = arrayDims
Else
GetDims = 0 'Array not initialized
End If
Else
GetDims = 0 'It's not an array... Type mismatch maybe?
End If
End Function