[RESOLVED] Hacking Variant
I'm trying to understand the data structure of the VB variant data type and am getting a puzzling result:
VB Code:
Sub VariantTest()
Dim vA(1 To 2) As Variant
Dim A(1 To 2) As Double, d1 As Double, d2 As Double
Dim vB As Variant
Dim itype As Integer
A(1) = 0.35
A(2) = 0.07
vA(1) = A(1)
vA(2) = A(2)
vB = vA
CopyMemory itype, vB(1), 2 'get the variant descriptor type
CopyMemory d1, ByVal VarPtr(vB(1)) + 8, 8 'get 1st value
CopyMemory d2, ByVal VarPtr(vB(1)) + 24, 8 'get 2nd value
Debug.Print TypeName(vB), itype, d1, d2 'Variant() 5 0.35 0.07
vB = A
CopyMemory itype, vB(1), 2 'get the variant descriptor type
CopyMemory d1, ByVal VarPtr(vB(1)) + 8, 8 'get 1st value
CopyMemory d2, ByVal VarPtr(vB(1)) + 24, 8 'get 2nd value
Debug.Print TypeName(vB), itype, d1, d2 'Double() 16389 3.75776923216955E-316 0
End Sub
If vB is assigned to the variant array (of doubles), the resulting data structure makes perfect sense. But, if vB is assigned to the array of doubles, the variant data structure makes no sense at all. The descriptor type is 16389 and I can't find the data values anywhere.
Can someone explain what's going on?
Re: [RESOLVED] Hacking Variant
Other mystery left is discrepancy between pvData and VarPtr(vB(1)) when vB = A
Re: [RESOLVED] Hacking Variant
Yeah, I agree. I did some more investigating and found that VarPtr(vB(0)) has a different meaning depending on the type of array that is assigned to vB!
If an array of Variant is assigned to vB, VarPtr(vB(0)) is the address of the 1st array element (variant data type). But, if an array of Long or Double is assigned to vB, VarPtr(vB(0)) is the address of what appears to be a variant data structure, but the value of the type field makes no sense. The address of the 1st array element is offset 8 bytes. Hmmm, just thought of something to be further investigated: what does VarPtr(vB(1)) represent?
Another curious tid bit: if an array is assigned to a vB, vB can be indexed like an array, typename indicates that vB is an array, but VarPtrArray(vB) produces an error because vB isn't an array.
There is another very odd thing I observed. If VarPtr(vB(0)) is assigned to a variable, say p1, and 16 bytes of memory values are extracted, the result is different if p1 is used vs VarPtr(vB(0)) even though p1 and VarPtr(vB(0)) evaluate to the same number! This has got to be the oddest thing I've ever seen! :sick:
Sure could use some Bruce McKinney expertise here. Anybody have a connection???? :D
Code:
Sub ArrayPointerTest2()
'Using ideas from Logophobic's code
Dim p1&, p2&, lValue1&, lValue2&
Dim dValue1#, dValue2#
Dim bt(15) As Byte
Dim itype1%, itype2%
Dim A(1) As Long '1D array of Long
Dim B(1) As Variant '1D array of Variant
Dim vB As Variant 'Variant
A(0) = 837& '1st array value
A(1) = 58& '2nd array value
B(0) = CDbl(2.37) '1st array value is Double
B(1) = CLng(12) '2nd array value is Long
vB = B 'assign array of Variant to vB
p1 = VarPtr(vB(0)) 'address 1st array element (variant)
CopyMemory bt(0), ByVal VarPtr(vB(0)), 16 'copy bytes from VarPtr(vB(0))
DisplayBytes bt, "Variant data structure" 'variant data structure
CopyMemory bt(0), ByVal p1, 16 'use p1 instead of VarPtr(vB(0))
DisplayBytes bt, "no difference" 'no difference
CopyMemory itype1, ByVal p1, 2 '5 = double
CopyMemory dValue1, ByVal p1 + 8, 8 '1st array value = double
p2 = p1 + 16 'address of 2nd array element (variant)
CopyMemory bt(0), ByVal p2, 16 'copy bytes
DisplayBytes bt, "Variant data structure" 'variant data structure
CopyMemory itype2, ByVal p2, 2 '3 = long
CopyMemory lValue1, ByVal p2 + 8, 4 '2nd array value = long
Debug.Print p1, p2, itype1, itype2, dValue1, lValue1
'VarPtr(vB(0)) is the address of the 1st array element
vB = A 'assign array of Long to vB
p1 = VarPtr(vB(0)) 'address of what?
CopyMemory bt(0), ByVal VarPtr(vB(0)), 16 'copy bytes from VarPtr(vB(0))
DisplayBytes bt, "What is this?" 'looks like variant data structure
CopyMemory itype1, ByVal VarPtr(vB(0)), 2 '16387 valid data type???
CopyMemory bt(0), ByVal p1, 16 'use p1 instead of VarPtr(vB(0))
DisplayBytes bt, "1st 2 bytes missing!" 'missing bytes, very strange!
CopyMemory p2, ByVal p1 + 8, 4 'address of 1st array element
CopyMemory lValue1, ByVal p2, 4 '1st array value
CopyMemory lValue2, ByVal p2 + 4, 4 '2nd array value
Debug.Print p1, p2, itype1, " ", lValue1, lValue2
'VarPtr(vB(0))is an address that is offset 8 bytes from the address of the 1st array element
'05 00 00 00 00 00 00 00 F6 28 5C 8F C2 F5 02 40 << Variant data structure
'05 00 00 00 00 00 00 00 F6 28 5C 8F C2 F5 02 40 << no difference
'03 00 00 00 00 00 00 00 0C 00 00 00 C2 F5 02 40 << Variant data structure
' 77036264 77036280 5 3 2.37 12
'03 40 00 00 00 00 00 00 20 55 97 04 00 00 00 00 << What is this?
'00 00 00 00 00 00 00 00 20 55 97 04 00 00 00 00 << 1st 2 bytes missing!
' 1308148 77026592 16387 837 58
End Sub
Private Sub DisplayBytes(btArr() As Byte, strText As String)
Dim i As Long
For i = LBound(btArr) To UBound(btArr)
If btArr(i) < 16 Then Debug.Print "0";
Debug.Print Hex(btArr(i)); " ";
Next i
Debug.Print "<< "; strText
End Sub
Re: [RESOLVED] Hacking Variant
I've tidied this up a touch, I'm pretty confident it can handle all the different kinds of Variant
Code:
Option Explicit
Public Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As Long)
Sub Main()
Dim Lng As Long
Dim LngArr() As Long
Dim Dbl As Double
Dim Byt As Byte
Dim Txt As String
Dim Txt3 As String * 3
Dim DblArr(2) As Double
Dim Obj As Object
Dim Var As Variant
Dim Dat As Date
Debug.Print "================================================="
DblArr(0) = 123
Dbl = 123
Var = DblArr
Set Obj = New StdPicture
DebugVariant Lng
DebugVariant LngArr
DebugVariant (LngArr)
DebugVariant Dbl
DebugVariant Var
DebugVariant Byt
DebugVariant Txt
DebugVariant Txt3
DebugVariant Obj
DebugVariant (Obj)
DebugVariant Dat
DebugVariant (Dat)
DebugVariant DblArr
DebugVariant (DblArr)
DebugVariant Var(0)
DebugVariant CDec("123456789012345678901234567")
End Sub
Public Sub DebugVariant(Var As Variant)
Dim VarByts(15) As Byte, Ptr As Long
Dim i As Long
RtlMoveMemory VarByts(0), ByVal VarPtr(Var), 16
Debug.Print "----------------------------"
Debug.Print "TypeName returns """ & TypeName(Var) & """"
Debug.Print "VarType() returns " & VarType(Var) & ", the High type byte is " & VarByts(1)
Select Case VarByts(1)
Case 0 'No high byte flag
Ptr = VarPtr(Var) + 8
Debug.Print "The variant contains the passed variable"
Debug.Print "The data resides at " & Ptr
Case 32 'Flag &H2000 (8192) IsArray
RtlMoveMemory Ptr, VarByts(8), 4
If Ptr Then
Debug.Print "The variant contains a pointer to a safearray structure"
Debug.Print "The Structure resides at " & Ptr
RtlMoveMemory Ptr, ByVal Ptr + 12, 4
Debug.Print "The first element resides at " & Ptr
Else
Debug.Print "The variant contains nothing the array is unintialised"
End If
Case 64 'Flag &H4000 (16384) IsPointer
Debug.Print "The variant contains a pointer to the sub variable"
RtlMoveMemory Ptr, VarByts(8), 4
Debug.Print "The data resides at " & Ptr
Case 96 'Flags &H6000 (24576) IsArray Or IsPointer
RtlMoveMemory Ptr, VarByts(8), 4
RtlMoveMemory Ptr, ByVal Ptr, 4
If Ptr Then
Debug.Print "The variant contains a pointer to a pointer to a safearray structure"
Debug.Print "The Structure resides at " & Ptr
RtlMoveMemory Ptr, ByVal Ptr + 12, 4
Debug.Print "The first element resides at " & Ptr
Else
Debug.Print "The variant contains a pointer to an unintialised array"
End If
Case Else
Debug.Print ":( Don't know what the variant contains. " & VarByts(1) * 256 & "?" 'let me know if you see this
End Select
''Return addresses confirmed by returning ptr as a function
''DebugVariant = Ptr 'return the address of the variable (first element if array)
'Extra bit to display the Variants bytes
For i = 15 To 0 Step -1
If i And 1 Then
Select Case i
Case 1: Debug.Print vbNewLine & " Type ";
Case 7: Debug.Print vbNewLine & "Reseved ";
Case 15: Debug.Print vbNewLine & " Data ";
Case Else: Debug.Print "|"; 'for hex output
End Select
End If
Debug.Print Right$("0" & Hex(VarByts(i)), 2); ' & " "; 'for hex output
'Debug.Print Format(VarByts(i), "000 "); 'for dec output'
Next i
Debug.Print
End Sub
If a decimal is passed the reserved bytes are used to make up the missing data (if required).
Re: [RESOLVED] Hacking Variant
I've tested the above by checking the addresses for the right data, it seems to work fine.
It does not explain (it just avoids) the weird stuff with VarPtr.