With a bit of memory trickery you can look at your multidimensional array as a one dimensional array. You will need LibMemory:
Option Explicit
Public Type FAKE_ARRAY
sArr As SAFEARRAY_1D
fakeArrPtr As LongPtr
values As Variant
End Type
Public Sub ArrayToFakeArray(ByRef arr As Variant, ByRef fakeArray As FAKE_ARRAY)
Dim aptr As LongPtr: aptr = ArrPtr(arr) 'Will throw if not array
Dim i As Long
'
With fakeArray
.fakeArrPtr = VarPtr(.sArr)
MemCopy .fakeArrPtr, aptr, LenB(.sArr)
With .sArr.rgsabound0
.cElements = 1
For i = 1 To fakeArray.sArr.cDims
.cElements = .cElements * (UBound(arr, i) - LBound(arr, i) + 1)
Next i
End With
.sArr.cDims = 1
.values = VarPtr(.fakeArrPtr)
MemInt(VarPtr(.values)) = VarType(arr) Or VT_BYREF
End With
End Sub
Quick test:
Sub Test()
Dim arr(2, 3, 2) As Variant
Dim i As Long, j As Long, k As Long
Dim m As Long
Dim v As Variant
'
For i = LBound(arr, 1) To UBound(arr, 1)
For j = LBound(arr, 2) To UBound(arr, 2)
For k = LBound(arr, 3) To UBound(arr, 3)
arr(i, j, k) = m
m = m + 1
Next k
Next j
Next i
'
Dim temp As FAKE_ARRAY: ArrayToFakeArray arr, temp
'
Dim arr2(1, 1) As Double
arr2(1, 1) = 17.55
'
Dim temp2 As FAKE_ARRAY: ArrayToFakeArray arr2, temp2
'
Debug.Print temp.values(0)
Debug.Print temp.values(4) '15
Debug.Print temp.values(35)
'
arr(1, 1, 0) = "AAA"
Debug.Print temp.values(4) 'AAA
Debug.Print temp2.values(3)
End Sub
Edit #1
This is a response to a series of interesting questions asked by the OP in the comments section. Not only the response is too long but it should definitely be part of the answer.
If I'm understanding correctly, the last line sets the array type as the same as arr but all the elements of the fake one point to the original ByRef?
When copying the SAFEARRAY structure, we also copy the pvData pointer pointing to the actual data. The fake array is pointing to the same data in memory so we're fooling the array handling code to read that data directly (not ByRef). But, we need to set the ByRef flag on the values Variant to avoid releasing the same memory twice which would cause a crash. But nothing so far is ByRef - just 2 array variables pointing to the same data.
Could there be a situation where the original is already ByRef (paramarray of VARIANTARGS?) and this doesn't work?
If the original data is having ByRef members (paramarray of VARIANTARGS) that could only happen if we use something like the CloneParamArray method because otherwise VB doesn't allow to pass the param array around, at least not natively. In this case, accessing the ByRef members via the fake array can only be done correctly via an utility function that can receive such a member ByRef.
Example:
Sub Test()
Dim t As Long: t = 5
ToParam 1, 2, 3, 4, t
Debug.Print t
End Sub
Public Sub ToParam(ParamArray args() As Variant)
Dim arr() As Variant
CloneParamArray args(0), UBound(args) + 1, arr
Dim temp As FAKE_ARRAY: ArrayToFakeArray arr, temp
Debug.Print arr(4)
' Debug.Print temp.values(4) 'Err 458 - type not supported
PrintVar temp.values(4)
args(4) = 7
Debug.Print arr(4)
PrintVar temp.values(4)
LetByRef(temp.values(4)) = 9
Debug.Print arr(4)
PrintVar temp.values(4)
End Sub
Private Function PrintVar(ByRef v As Variant)
Debug.Print v
End Function
Private Property Let LetByRef(ByRef vLeft As Variant, ByVal vRight As Variant)
vLeft = vRight
End Property
If one uses CloneParamArray on purpose then one should be aware anyway that the ByRef individual Variant members can only be accesed/changed via utility methods like PrintVar and LetByRef or any other method expecting a ByRef Variant as an argument.
Also, why do you take the array byref? Because it's a reference type, byval doesn't make a shallow copy so the only difference is now you can set arr to point to a different array?
Because we don't know what the array type is (e.g. Long() or Variant()) then we obviously must wrap in a Variant when we pass to the ArrayToFakeArray method. Passing the wrapped array ByVal does make a copy and we can see that by runing the below:
Option Explicit
Sub Test()
Dim arr() As Long
ReDim arr(0 To 1)
arr(0) = 12
arr(1) = 44
'
PassArr arr, arr
Debug.Print
Debug.Print arr(1) 'Prints 55
End Sub
Private Sub PassArr(ByVal arrByVal As Variant, ByRef arrByRef As Variant)
#If Win64 Then
Const dataOffset As Long = 16
#Else
Const dataOffset As Long = 12
#End If
Dim aPtrByVal As LongPtr: aPtrByVal = ArrPtr(arrByVal)
Dim aPtrByRef As LongPtr: aPtrByRef = ArrPtr(arrByRef)
Dim pvDataByVal As LongPtr: pvDataByVal = MemLongPtr(aPtrByVal + dataOffset)
Dim pvDataByRef As LongPtr: pvDataByRef = MemLongPtr(aPtrByRef + dataOffset)
'
Debug.Print "ByVal SAFEARRAY address:", aPtrByVal, "ByVal data address:", pvDataByVal
Debug.Print "ByRef SAFEARRAY address:", aPtrByRef, "ByRef data address:", pvDataByRef
'
Debug.Print MemLong(pvDataByVal + 4) 'Prints 44
Debug.Print MemLong(pvDataByRef + 4) 'Prints 44
'
arrByRef(1) = 55
'
Debug.Print MemLong(pvDataByVal + 4) 'Prints 44
Debug.Print MemLong(pvDataByRef + 4) 'Prints 55
'
arrByVal(1) = 77
'
Debug.Print MemLong(pvDataByVal + 4) 'Prints 77
Debug.Print MemLong(pvDataByRef + 4) 'Prints 55
End Sub
So, we need to pass the wrapped array ByRef so that ArrPtr returns the correct address of the SAFEARRAY structure.