【发布时间】:2017-04-21 21:19:03
【问题描述】:
对于每个元素都是双精度数组的变体数组,我可以执行以下操作:
Public Declare PtrSafe Sub CopyMemoryArray Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination() As Any, ByRef Source As Any, ByVal Length As Long)
Sub test()
Dim vntArr() as Variant
Dim A() as Double
Dim B() as Double
Redim vntArr(1 to 10)
Redim A(1 to 100, 1 to 200)
vntArr(1) = A
CopyMemoryArray B, ByVal VarPtr(vntArr(1)) + 8, PTR_LENGTH '4 or 8
'Do something
ZeroMemoryArray B, PTR_LENGTH
End Sub
A 和 B 将指向内存中的同一个块。 (设置 W = vntArr(1) 会创建一个副本。对于非常大的数组,我想避免这种情况。)
我正在尝试做同样的事情,但使用集合:
Sub test()
Dim col as Collection
Dim A() as Double
Dim B() as Double
Set col = New Collection
col.Add A, "A"
CopyMemoryArray B, ByVal VarPtr(col("A")) + 8, PTR_LENGTH '4 or 8
'Do something
ZeroMemoryArray B, PTR_LENGTH
End Sub
这种方法可行,但由于某种原因,由 col("A") 返回的安全数组结构(包装在 Variant 数据类型中,类似于上面的变体数组)仅包含一些外部属性,例如维度数和模糊边界,但指向pvData 的指针本身为空,因此调用 CopyMemoryArray 会导致崩溃。 (设置 B = col("A") 工作正常。)与 Scripting.Dictionary 的情况相同。
编辑
#If Win64 Then
Public Const PTR_LENGTH As Long = 8
#Else
Public Const PTR_LENGTH As Long = 4
#End If
Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Const VT_BYREF As Long = &H4000&
Private Const S_OK As Long = &H0&
Private Function pArrPtr(ByRef arr As Variant) As LongPtr
Dim vt As Integer
CopyMemory vt, arr, 2
If (vt And vbArray) <> vbArray Then
Err.Raise 5, , "Variant must contain an array"
End If
If (vt And VT_BYREF) = VT_BYREF Then
CopyMemory pArrPtr, ByVal VarPtr(arr) + 8, PTR_LENGTH
CopyMemory pArrPtr, ByVal pArrPtr, PTR_LENGTH
Else
CopyMemory pArrPtr, ByVal VarPtr(arr) + 8, PTR_LENGTH
End If
End Function
Private Function GetPointerToData(ByRef arr As Variant) As LongPtr
Dim pvDataOffset As Long
#If Win64 Then
pvDataOffset = 16 '4 extra unused bytes on 64bit machines
#Else
pvDataOffset = 12
#End If
CopyMemory GetPointerToData, ByVal pArrPtr(arr) + pvDataOffset, PTR_LENGTH
End Function
Sub CollectionWorks()
Dim A(1 To 100, 1 To 50) As Double
A(3, 1) = 42
Dim c As Collection
Set c = New Collection
c.Add A, "A"
Dim ActualPointer As LongPtr
ActualPointer = GetPointerToData(c("A"))
Dim r As Double
CopyMemory r, ByVal ActualPointer + (0 + 2) * 8, 8
MsgBox r 'Displays 42
End Sub
【问题讨论】:
-
不会深入研究它,但这可能是因为一个has
VT_BYREF而另一个没有。参见例如stackoverflow.com/q/11713408/11683 以更稳定的方式创建引用相同数据的数组。 -
嗯,不,vntArr(1) 和 col("A") 都具有相同的 VarType = 8197 = 8192 (Array) + 5 (Double)。使用 VT_BYREF 应该是 16384+8197
-
确定一下,你用的是VB的
VarType吗?它隐藏了VT_BYREF,这就是为什么我必须像上面那样手动操作。 -
我正在直接查看变体结构中的类型...在主帖中编辑
-
你必须将数组直接存储在集合中吗?我只需创建一个类来保存数组和返回指针的函数,然后将类推送到集合中。