18

VBA (または VB6) で配列参照をコピーする方法はありますか?

VBA では、配列は値型です。ある配列変数を別の配列変数に代入すると、配列全体がコピーされます。同じ配列を指すように 2 つの配列変数を取得したいと考えています。おそらくいくつかの API メモリ関数や関数を使用VarPtrして、実際に 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をメソッドの複数のパラメーターに渡すことで、同じ配列への複数の参照を取得することができます。

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)

しかし、それでも、既存のものと同じスコープで新しいリファレンスを単純に製造することはできません。

4

4 に答える 4

22

はい、両方の変数が Variant 型である場合は可能です。

理由は次のとおりです。 Variant 型自体がラッパーです。Variant の実際のビット コンテンツは 16 バイトです。最初のバイトは、現在格納されている実際のデータ型を示します。値は VbVarType 列挙に正確に対応します。つまり、Variant が現在 Long 値を保持している場合、最初のバイトは0x03の値になりますvbLong。2 番目のバイトには、いくつかのビット フラグが含まれます。たとえば、バリアントに配列が含まれている場合、0x20このバイトのビット at が設定されます。

残りの 14 バイトの使用は、格納されるデータ タイプによって異なります。どの配列タイプでも、配列のアドレスが含まれます。

つまり、を使用して 1 つのバリアントのを直接上書きすると、実際には配列への参照RtlMoveMemoryが上書きされます。これは実際に機能します!

注意点が 1 つあります。配列変数がスコープ外になると、VB ランタイムは実際の配列要素に含まれていたメモリを再利用します。先ほど説明した Variant CopyMemory 手法を使用して配列参照を手動で複製した場合、その結果、両方のバリアントがスコープ外になると、ランタイムが同じメモリを 2 回再利用しようとし、プログラムがクラッシュします。これを回避するには、変数がスコープ外になる前に、バリアントを 0 などで再度上書きして、1 つを除くすべての参照を手動で「消去」する必要があります。

例 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
于 2013-05-02T17:10:52.087 に答える
1

この解決策はどうですか...

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

Public Sub TRIAL()
Dim myValueType As Integer
Dim mySecondValueType As Integer
Dim memPTR As Long

myValueType = 67
memPTR = VarPtr(mySecondValueType)
CopyMemory ByVal memPTR, myValueType, 2
Debug.Print mySecondValueType
End Sub

この概念は、ここの CodeProject の記事から来ました

于 2013-05-01T19:03:34.607 に答える