在VBA中复制阵列参考 [英] Copy an array reference in VBA

查看:153
本文介绍了在VBA中复制阵列参考的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有什么方法可以在VBA(或VB6)中复制数组引用吗?

Is there any way to copy an array reference in VBA (or VB6)?

在VBA中,数组是值类型.将一个数组变量分配给另一个数组变量将复制整个数组.我想让两个数组变量指向同一数组.有什么方法可以做到这一点,也许使用某些API内存函数和/或VarPtr函数,实际上会返回VBA中变量的地址?

In VBA, arrays are value types. Assigning one array variable to another copies the entire array. I want to get two array variables to point to the same array. Is there any way to accomplish this, perhaps using some API memory functions and/or the VarPtr function, which does in fact return the address of a variable in VBA?

Dim arr1(), arr2(), ref1 As LongPtr
arr1 = Array("A", "B", "C")

' Now I want to make arr2 refer to the same array object as arr1
' If this was C#, simply assign, since in .NET arrays are reference types:
arr2 = arr1

' ...Or if arrays were COM objects:
Set arr2 = arr1

' VarPtr lets me get the address of arr1 like this:
ref1 = VarPtr(arr1)

' ... But I don't know of a way to *set* address of arr2.

顺便说一句,可以通过将相同的数组变量ByRef传递给方法的多个参数来获得对同一数组的多个引用:

Incidentally, it is possible to get multiple references to the same array by passing the same array variable ByRef to multiple parameters of a method:

Sub DuplicateRefs(ByRef Arr1() As String, ByRef Arr2() As String)
    Arr2(0) = "Hello"
    Debug.Print Arr1(0)
End Sub

Dim arrSource(2) As String
arrSource(0) = "Blah"

' This will print 'Hello', because inside DuplicateRefs, both variables
' point to the same array. That is, VarPtr(Arr1) == VarPtr(Arr2)
Call DuplicateRefs(arrSource, arrSource)

但是,这仍然不允许人们简单地在与现有参考文献相同的范围内制造新的参考文献.

But this still does not allow one to simply manufacture a new reference in the same scope as an existing one.

推荐答案

是的,如果两个变量均为Variant类型,则可以.

原因如下:Variant类型本身就是包装器.变体的实际位内容是16个字节.第一个字节指示当前存储的实际数据类型.该值完全对应于VbVarType枚举.即如果Variant当前持有Long值,则第一个字节为0x03,即vbLong的值.第二个字节包含一些位标志.例如,如果变量包含数组,则将设置此字节中0x20的位.

Here's why: The Variant type is itself a wrapper. The actual bit content of a Variant is 16 bytes. The first byte indicates the actual data type currently stored. The value corresponds exactly the VbVarType enum. I.e if the Variant is currently holding a Long value, the first byte will be 0x03, the value of vbLong. The second byte contains some bit flags. For exampe, if the variant contains an array, the bit at 0x20 in this byte will be set.

剩余的14个字节的使用取决于要存储的数据类型.对于任何数组类型,它都包含数组的地址.

The use of the remaining 14 bytes depends on the data type being stored. For any array type, it contains the address of the array.

这意味着,如果您使用RtlMoveMemory直接覆盖一个变体的 value ,则实际上已将 reference 覆盖了一个数组.实际上,这确实有效!

That means if you directly overwrite the value of one variant using RtlMoveMemory you have in effect overwritten the reference to an array. This does in fact work!

有一个警告:当数组变量超出范围时,VB运行时将回收实际数组元素所包含的内存.当您通过我刚刚描述的Variant CopyMemory技术手动复制了数组引用时,结果是当两个变量都超出范围时,运行时将尝试两次回收相同的内存,并且程序将崩溃.为避免这种情况,您需要在变量超出范围之前,通过再次覆盖变体(例如用0代替)来手动擦除"除其中一个引用以外的所有引用.

There's one caveat: When an array variable goes out of scope, the VB runtime will reclaim the memory that the actual array elements contained. When you have manually duplicated an array reference via the Variant CopyMemory technique I've just described, the result is that the runtime will try to reclaim that same memory twice when both variants go out of scope, and the program will crash. To avoid this, you need to manually "erase" all but one of the references by overwriting the variant again, such as with 0s, before the variables go out of scope.

示例1:此方法有效,但是一旦两个变量都超出范围(当子退出时)就会崩溃

Private Declare PtrSafe Sub CopyMemory Lib "kernel32" _
    Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Sub CopyArrayRef_Bad()
    Dim v1 As Variant, v2 As Variant
    v1 = Array(1, 2, 3)
    CopyMemory v2, v1, 16

    ' Proof:
    v2(1) = "Hello"
    Debug.Print Join(v1, ", ")

    ' ... and now the program will crash
End Sub

示例2:经过仔细的清理,您可以摆脱它!

Private Declare PtrSafe Sub CopyMemory Lib "kernel32" _
    Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Private Declare PtrSafe Sub FillMemory Lib "kernel32" _
    Alias "RtlFillMemory" (Destination As Any, ByVal Length As Long, ByVal Fill As Byte)

Sub CopyArrayRef_Good()
    Dim v1 As Variant, v2 As Variant
    v1 = Array(1, 2, 3)
    CopyMemory v2, v1, 16

    ' Proof:
    v2(1) = "Hello"
    Debug.Print Join(v1, ", ")

    ' Clean up:
    FillMemory v2, 16, 0

    ' All good!
End Sub

这篇关于在VBA中复制阵列参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆