[RESOLVED] How to get the memory-pointer of a byte-array variable?-VBForums
Results 1 to 17 of 17

Thread: [RESOLVED] How to get the memory-pointer of a byte-array variable?

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Sep 2012
    Posts
    814

    Resolved [RESOLVED] How to get the memory-pointer of a byte-array variable?

    I know that a byte array pointer can be obtained through VarPtr(byte_Array(0)). However, when the byte_Array is passed as a variable to another function, VarPtr(byte_Array(0)) does not get the memory pointer to the byte_Array. I don't know why.

    Code:
    Option Explicit
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
        ByRef Destination As Any, _
        ByRef Source As Any, _
        ByVal Length As Long)
        
    Private m_nByteArrayPtr As Long
    
    Private Sub Form_Click()
        Dim arrBytes() As Byte
        ReDim arrBytes(100) As Byte
        
        m_nByteArrayPtr = VarPtr(arrBytes(0))
        
        DoTest arrBytes
        
    End Sub
    
    Private Sub DoTest(FileNameOrByteArray)
        If VarType(FileNameOrByteArray) = vbString Then Exit Sub
        
        Dim nPtr1 As Long
        Dim nPtr2 As Long
        
        nPtr1 = VarPtr(FileNameOrByteArray)
        nPtr2 = VarPtr(FileNameOrByteArray(0))
            
        If nPtr1 <> m_nByteArrayPtr Then
            MsgBox "'VarPtr(FileNameOrByteArray)' is not the pointer of the byte array !"
        End If
        
        If nPtr2 <> m_nByteArrayPtr Then
            MsgBox "'VarPtr(FileNameOrByteArray(0))' is not the pointer of the byte array !"
        End If
            
    End Sub

  2. #2
    PowerPoster
    Join Date
    Oct 2013
    Posts
    3,051

    Re: How to get the memory-pointer of a byte-array variable?

    You are passing it as Variant.
    Have you tried what happens if you really make Byte only version?

    Code:
    Private Sub DoTest(FileNameOrByteArray() As Byte)

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Sep 2012
    Posts
    814

    Re: How to get the memory-pointer of a byte-array variable?

    Hi Arnoutdv, I must pass it as a variant, because the variable might be a string (file-name) or could be a byte-array.
    Last edited by dreammanor; Jan 11th, 2018 at 05:49 AM.

  4. #4
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    17,093

    Re: How to get the memory-pointer of a byte-array variable?

    Gets a bit more complex. Within the variant, 8 bytes from its pointer, is a pointer to a SafeArray structure for that byte array. That structure contains the memory address of the first array item. Simple math, from there, allows calculating the address of any item in the array.

    Edited: If you haven't seen this, it is worth bookmarking and referring to from time to time.
    Last edited by LaVolpe; Jan 11th, 2018 at 06:52 AM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  5. #5
    Fanatic Member
    Join Date
    Feb 2015
    Posts
    960

    Re: How to get the memory-pointer of a byte-array variable?

    For referenced parameters (BSTR, SAFEARRAY, Object, IUnknown, etc.):
    Code:
    Private Function VarPtrVar( _
                     ByRef v As Variant) As Long
        Dim vt  As Long
        Dim ptr As Long
        
        GetMem4 v, vt
        GetMem4 ByVal VarPtr(v) + 8, ptr
        
        If vt And &H4000 Then GetMem4 ByVal ptr, ptr
        
        If vt And &H2000 Then
            ' // sa
            Dim cDim As Long
            
            GetMem4 ByVal ptr, cDim
            
            If (cDim And &HFFFF&) = 0 Then Exit Function
            
            GetMem4 ByVal ptr + &HC, ptr
    
        End If
        
        VarPtrVar = ptr
             
    End Function
    If you want to use any parameter (Long, Integer, Byte, Double, etc.) you should check the vt value and if it isn't referenced return VarPtr(v) + 8 (if VT_BYREF was not specified, else just get that value).

  6. #6
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,862

    Re: How to get the memory-pointer of a byte-array variable?

    Code:
    Option Explicit
    
    Private Declare Function RefVarAry Lib "msvbvm60" Alias "__vbaRefVarAry" (ArrayVar) As Long
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
        ByRef Destination As Any, _
        ByRef Source As Any, _
        ByVal Length As Long)
        
    Private m_nByteArrayPtr As Long
    
    Private Property Get DeRef(ByVal Address As Long) As Long
        GetMem4 ByVal Address, DeRef
    End Property
    Private Property Let DeRef(ByVal Address As Long, ByVal Value As Long)
        GetMem4 Value, ByVal Address
    End Property
    Private Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function
    
    
    Private Sub Form_Click()
        Dim arrBytes() As Byte
        ReDim arrBytes(100) As Byte
        
        m_nByteArrayPtr = VarPtr(arrBytes(0))
        
        DoTest arrBytes
        
    End Sub
    
    Private Sub DoTest(FileNameOrByteArray)
        Select Case VarType(FileNameOrByteArray)
            Case vbString
                Exit Sub
                
            Case vbByte Or vbArray
            
                Dim nPtr1 As Long
                Dim nPtr2 As Long
                Dim nPtr3 As Long
                
                nPtr3 = DeRef(PtrAdd(DeRef(RefVarAry(FileNameOrByteArray)), 12))
                
                nPtr1 = VarPtr(FileNameOrByteArray)
                nPtr2 = VarPtr(FileNameOrByteArray(0))
                
                If nPtr3 <> m_nByteArrayPtr Then
                    MsgBox "DeRef(PtrAdd(DeRef(RefVarAry(FileNameOrByteArray)), 12)) is not the pointer of the byte array !"
                End If
    
                    
                If nPtr1 <> m_nByteArrayPtr Then
                    MsgBox "'VarPtr(FileNameOrByteArray)' is not the pointer of the byte array !"
                End If
                
                If nPtr2 <> m_nByteArrayPtr Then
                    MsgBox "'VarPtr(FileNameOrByteArray(0))' is not the pointer of the byte array !"
                End If
                
            Case Else
                Err.Raise 5
                
        End Select
    End Sub
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  7. #7
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    4,012

    Re: How to get the memory-pointer of a byte-array variable?

    dreammanor,

    Here are some procedures I somewhat recently put together. I think I've put them through all their paces, but they're not thoroughly exercised in all possible situations. I suppose this is as good a time as any to throw them out here. If you "think through them", you should find the answers to getting your variable pointer. As LaVolpe suggested, there are several issues to be considered.

    For a BAS module:
    Code:
    
    Option Explicit
    '
    Public Type SAFEARRAY0D
        cDims As Integer
        fFeatures As Integer
        cbElements As Long
        cLocks As Long
        pvData As Long
    End Type
    Public Type SAFEARRAYBOUND
        cElements As Long
        lLbound As Long
    End Type
    '
    Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
    '
    
    Public Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
        ' For adding (or subtracting) a small number from a pointer.
        ' Use PtrAddEx for adding (or subtracting) large numbers from a pointer.
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function
    
    Public Sub GetSafeArrayInfo(v As Variant, sa As SAFEARRAY0D, sab() As SAFEARRAYBOUND)
        Dim pSafeArr As Long
        '
        sa = SafeArrayStruct(v)
        If sa.cDims = 0 Then Exit Sub ' No dimensions, not dimensioned.
        '
        ReDim sab(0 To sa.cDims - 1)
        '
        ' Now we can get the remainder of the SAFEARRAY structure.
        CopyMemory sab(0), ByVal (pSafeArr + LenB(sa)), CLng(sa.cDims * 8)
    End Sub
    
    Public Function SafeArrayStruct(vArray As Variant, Optional pSafeArr As Long) As SAFEARRAY0D
        ' Just returns if it's not an array.  You should ideally check this before calling here.
        ' It will return the SafeArrayStruct of basically any array.
        ' If passed in, pSafeArr can be used as a return.
        '
        Dim VarType As Integer
        Dim ppSafeArr As Long
        '
        If Not IsArray(vArray) Then Exit Function
        CopyMemory VarType, vArray, 2                                   ' Just get the Variant type from its 16 bytes.
        '
        If VarType And &H4000 Then                                      ' It's a ByRef type Variant array.
            CopyMemory ppSafeArr, ByVal PtrAdd(VarPtr(vArray), 8&), 4&  ' Get pointer to the array inside the Variant structure.
            CopyMemory pSafeArr, ByVal ppSafeArr, 4&                    ' Dereference ppSafeArr.
        Else                                                            ' It's a ByVal type Variant array.
            CopyMemory pSafeArr, ByVal PtrAdd(VarPtr(vArray), 8&), 4&   ' Get pointer to the array inside the Variant structure.
        End If
        If pSafeArr = 0& Then Exit Function
        CopyMemory SafeArrayStruct, ByVal pSafeArr, LenB(SafeArrayStruct)
    End Function
    
    Enjoy,
    Elroy

    EDIT1: I polished it up a bit to use the PtrAdd approach shown to us by Dex.

    EDIT2: Also notice the Optional pSafeArr argument for the SafeArrayStruct call. That's precisely what you asked for.

    EDIT3: You could also just call that SafeArrayStruct like a SUB, and get that value:

    Tested in Form1:
    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim ptr As Long
    
        Dim arr(1 To 5) As Long
    
        Call SafeArrayStruct(arr, ptr)
    
        MsgBox ptr
    
    End Sub
    
    Last edited by Elroy; Jan 11th, 2018 at 10:09 AM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  8. #8
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    4,012

    Re: How to get the memory-pointer of a byte-array variable?

    @Dex: You're doing some interesting comparisons, but I don't see where you're addressing the ByRef/ByVal issue? And, you're definitely doing the PtrAdd in a better way than me, but I'm not sure that'll ever be a problem for VB6, or at least not except in extreme situations. But I suppose I should fix that (now fixed).
    Last edited by Elroy; Jan 11th, 2018 at 10:04 AM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  9. #9
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,862

    Re: How to get the memory-pointer of a byte-array variable?

    Quote Originally Posted by Elroy View Post
    @Dex: You're doing some interesting comparisons, but I don't see where you're addressing the ByRef/ByVal issue? And, you're definitely doing the PtrAdd in a better way than me, but I'm not sure that'll ever be a problem for VB6, or at least not except in extreme situations. But I suppose I should fix that (now fixed).
    the runtime function RefVarAry() handles byref automatically.
    like you said PtrAdd is required only in extreme situations. You have to set the LARGEADDRESSAWARE flag in the executable so that VB can address the whole 4GB range, and it has to be running on 64bit windows. I think that's the only situation where the heap can span over the 2GB boundary of the process Virtual Address space.
    Function RefVarAry(ByRef TheArray As Variant) As Long
    TheArray
    An Array variable packed in a Variant. Will throw a Runtime Error 5 (Invalid Parameter) if TheArray is not an Array type.
    Returns
    a pointer to the referenced array variable. Automatically Derefences if the array is passed ByRef. Dereference the returned pointer to get the address of the SAFEARRAY structure, or use AryPtr()

    more fun with pointers, using my typelib.

    Code:
    Private Sub Form_Load()
    
        Dim TestArray(0) As Byte
        Dim V: V = TestArray ' Makes a copy!
        
        Debug.Print "VarVT            ", Hex$(VarVT(TestArray))
        Debug.Print "VarVT(Copy)      ", Hex$(VarVT(V))
        Debug.Print "Ary|Byte         ", Hex$(vbArray Or vbByte)
        Debug.Print "ByRef|Ary|Byte   ", Hex$(vbArray Or vbByte Or vbByRef)
        Debug.Print "RefAry           ", PtrHex$(RefAry(TestArray))
        Debug.Print "RefVarAry        ", PtrHex$(RefVarAry(TestArray))
        Debug.Print "*RefAry          ", PtrHex$(DeRef(RefAry(TestArray)))
        Debug.Print "*RefVarAry       ", PtrHex$(DeRef(RefVarAry(TestArray)))
        Debug.Print "AryPtr           ", PtrHex$(AryPtr(TestArray))
        Debug.Print "*(AryPtr+12)     ", PtrHex$(DeRef(PtrAdd(AryPtr(TestArray), 12)))
        Debug.Print "*(*RefVarAry+12) ", PtrHex$(DeRef(PtrAdd(DeRef(RefVarAry(TestArray)), 12)))
        Debug.Print "&Ary(0)          ", PtrHex$(Ref(TestArray(0)))
        Test TestArray
        
        Debug.Print "TestArray(0)     ", TestArray(0)
        Unload Me
    End Sub
    
    Private Sub Test(ByRef TestArray)
        Debug.Print
        Debug.Print "VarVT            ", Hex$(VarVT(TestArray))
        Debug.Print "ByRef|Ary|Byte   ", Hex$(vbArray Or vbByte Or vbByRef)
        Debug.Print "RefVarAry        ", PtrHex$(RefVarAry(TestArray))
        Debug.Print "*RefVarAry       ", PtrHex$(DeRef(RefVarAry(TestArray)))
        Debug.Print "*(*RefVarAry+12) ", PtrHex$(DeRef(PtrAdd(DeRef(RefVarAry(TestArray)), 12)))
        Debug.Print "&Ary(0)          ", PtrHex$(Ref(TestArray(0))) & " <---- address of temp variable!"
        
        DByte(DeRef(PtrAdd(DeRef(RefVarAry(TestArray)), 12))) = 255
    End Sub
    
    Public Function PtrHex$(ByVal Address As Long)
        PtrHex = Right$(String$(7, "0") & Hex$(Address), 8)
    End Function
    
    Private Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function
    Code:
    VarVT                       6011
    VarVT(Copy)                 2011
    Ary|Byte                    2011
    ByRef|Ary|Byte              6011
    RefAry                      0013F870
    RefVarAry                   0013F870
    *RefAry                     0013F898
    *RefVarAry                  0013F898
    AryPtr                      0013F898
    *(AryPtr+12)                00237950
    *(*RefVarAry+12)            00237950
    &Ary(0)                     00237950
    
    VarVT                       6011
    ByRef|Ary|Byte              6011
    RefVarAry                   0013F870
    *RefVarAry                  0013F898
    *(*RefVarAry+12)            00237950
    &Ary(0)                     0013F778 <---- address of temp variable!
    TestArray(0)                 255
    Last edited by DEXWERX; Jan 11th, 2018 at 02:59 PM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  10. #10
    PowerPoster
    Join Date
    Feb 2006
    Posts
    18,571

    Re: How to get the memory-pointer of a byte-array variable?

    Nothing original here I see, but this was what I did and it seems to work:

    Code:
    Option Explicit
    
    Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal Pointer As Long, RetVal As Long)
    
    Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyW" ( _
        ByVal lpString1 As Long, _
        ByVal lpString2 As Long) As Long
    
    Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" ( _
        ByVal lpString As Long) As Long
    
    Private Function PtrOfVValue(ByRef V As Variant) As Long
        'Extract a "pointer to sz" from a Variant argument.
    
        'Coerce anything but a String or Byte array to a String value:
        Select Case VarType(V)
            Case vbObject
                V = TypeName(V)
                'Or instead: Err.Raise 380 'Invalid property value.
            Case Is <> vbString, Is <> (vbByte Or vbArray)
                V = CStr(V)
                'Or instead: Err.Raise 380 'Invalid property value.
        End Select
    
        GetMem4 &H80000000 Xor ((VarPtr(V) Xor &H80000000) + 8), PtrOfVValue
        If VarType(V) = (vbByte Or vbArray) Then
            GetMem4 &H80000000 Xor ((PtrOfVValue Xor &H80000000) + 12), PtrOfVValue
        End If
    End Function
    
    Private Sub Display(ByRef TestCase As String, ByVal V As Variant)
        'Simple test of the PtrOfVValue() function.
        '
        'Relies upon both String and Byte array values having a "guard NUL" at the end.
        Dim Pointer As Long
        Dim Value As String
    
        Pointer = PtrOfVValue(V)
        Value = Space$(lstrlen(Pointer))
        lstrcpy StrPtr(Value), Pointer
        Print TestCase, "["; Value; "]"
    End Sub
    
    Private Sub Form_Load()
        Dim S As String
        Dim B() As Byte
    
        S = "Hello World!"
        Display "String", S
        B = S
        Display "Byte array", B
        Display "Object", Me
        Display "Date", Now()
        Display "Double", -1.46987654321E-58
    End Sub
    Name:  sshot.png
Views: 153
Size:  2.4 KB
    Attached Files Attached Files

  11. #11
    PowerPoster
    Join Date
    Jun 2013
    Posts
    3,551

    Re: How to get the memory-pointer of a byte-array variable?

    Quote Originally Posted by dilettante View Post
    ...this was what I did and it seems to work:
    Nah, not really.

    Your approach falls apart, as soon as you change the signature of your test-routine:
    Code:
    Private Sub Display(ByRef TestCase As String, ByVal V As Variant)
    to:
    Code:
    Private Sub Display(ByRef TestCase As String, ByRef V As Variant)
    Half of the discussion further above was related to that VT_ByRef-scenario.

    Here's what works (using a few parts from what DexWerx has posted).
    Code:
     
    Option Explicit
    
    Private Declare Function RefVarAry Lib "msvbvm60" Alias "__vbaRefVarAry" (ArrayVar) As Long
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    
    Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyW" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
    Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" (ByVal lpString As Long) As Long
    
    Private Property Get DeRef(ByVal Address As Long) As Long
        GetMem4 ByVal Address, DeRef
    End Property
    
    Private Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function
    
    Private Function StrOrBytesPtr(V) As Long 'returns Zero for unsupported types
        Select Case VarType(V)
          Case vbString:          StrOrBytesPtr = StrPtr(V)
          Case vbByte Or vbArray: StrOrBytesPtr = DeRef(PtrAdd(DeRef(RefVarAry(V)), 12))
        End Select
    End Function
    
    Private Sub DisplayByVal(ByRef TestCase As String, ByVal V As Variant)
        Dim Pointer As Long, Value As String
    
        Pointer = StrOrBytesPtr(V): If Pointer = 0 Then Exit Sub
        Value = Space$(lstrlen(Pointer))
        lstrcpy StrPtr(Value), Pointer
        Print TestCase, "["; Value; "]"
    End Sub
    
    Private Sub DisplayByRef(ByRef TestCase As String, ByRef V As Variant)
        Dim Pointer As Long, Value As String
    
        Pointer = StrOrBytesPtr(V): If Pointer = 0 Then Exit Sub
        Value = Space$(lstrlen(Pointer))
        lstrcpy StrPtr(Value), Pointer
        Print TestCase, "["; Value; "]"
    End Sub
    
    Private Sub Form_Load()
        AutoRedraw = True
        
        Dim S As String
        S = "Hello World!" & vbNullChar
        DisplayByVal "String-Value ByVal", S
        DisplayByRef "String-Value ByRef", S
        
        Dim B() As Byte
        B = S
        DisplayByVal "Byte-Array ByVal:", B
        DisplayByRef "Byte-Array ByRef:", B
    End Sub
    Olaf

  12. #12
    PowerPoster
    Join Date
    Feb 2006
    Posts
    18,571

    Re: How to get the memory-pointer of a byte-array variable?

    Ahh, I didn't get that from the discussion above. A simple fix handles that:

    Code:
    Option Explicit
    
    Private Declare Sub GetMem2 Lib "msvbvm60" (ByVal Pointer As Long, RetVal As Integer)
    
    Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal Pointer As Long, RetVal As Long)
    
    Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyW" ( _
        ByVal lpString1 As Long, _
        ByVal lpString2 As Long) As Long
    
    Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" ( _
        ByVal lpString As Long) As Long
    
    Private Function PtrOfVValue(ByRef V As Variant) As Long
        'Extract a "pointer to sz" from a Variant argument.
        Const VT_BYREF As Integer = &H4000
        Dim VarTypeOfV As VbVarType
        Dim RawVarType As Integer
    
        VarTypeOfV = VarType(V)
        If Not (VarTypeOfV = vbString Or VarTypeOfV = (vbByte Or vbArray)) Then
            Err.Raise 380 'Invalid property value.
        End If
    
        If VarTypeOfV = vbString Then
            PtrOfVValue = StrPtr(V)
        Else
            GetMem4 &H80000000 Xor ((VarPtr(V) Xor &H80000000) + 8), PtrOfVValue
            GetMem2 VarPtr(V), RawVarType
            If RawVarType And VT_BYREF Then GetMem4 PtrOfVValue, PtrOfVValue
            GetMem4 &H80000000 Xor ((PtrOfVValue Xor &H80000000) + 12), PtrOfVValue
        End If
    End Function
    
    Private Sub DisplayByRef(ByRef TestCase As String, ByRef V As Variant)
        'Simple test of the PtrOfVValue() function.
        Dim Pointer As Long
        Dim Value As String
    
        Pointer = PtrOfVValue(V)
        Value = Space$(lstrlen(Pointer))
        lstrcpy StrPtr(Value), Pointer
        Print TestCase; Tab(20); "["; Value; "]"
    End Sub
    
    Private Sub DisplayByVal(ByRef TestCase As String, ByVal V As Variant)
        'Simple test of the PtrOfVValue() function.
        Dim Pointer As Long
        Dim Value As String
    
        Pointer = PtrOfVValue(V)
        Value = Space$(lstrlen(Pointer))
        lstrcpy StrPtr(Value), Pointer
        Print TestCase; Tab(20); "["; Value; "]"
    End Sub
    
    Private Sub Form_Load()
        Dim S As String
        Dim B() As Byte
    
        S = "Hello World!"
        B = S & vbNullChar
        DisplayByRef "ByRef String", S
        DisplayByRef "ByRef Byte array", B
        Print
        DisplayByVal "ByVal String", S
        DisplayByVal "ByVal Byte array", B
    End Sub
    Attached Files Attached Files

  13. #13
    PowerPoster
    Join Date
    Jun 2013
    Posts
    3,551

    Re: How to get the memory-pointer of a byte-array variable?

    And to harden it for the "passing of an uninitialized ByteArray-Type",
    you'd have to enhance in a somewhat similar way as shown below:

    Code:
    Private Function StrOrBytesPtr(V) As Long 'returns Zero for unsupported types or when Strings or Arrays are uninitialized
      Select Case VarType(V)
        Case vbString: StrOrBytesPtr = StrPtr(V)
        Case vbByte Or vbArray
             Dim Ptr As Long: Ptr = DeRef(RefVarAry(V))
             If Ptr Then StrOrBytesPtr = DeRef(PtrAdd(Ptr, 12))
      End Select
    End Function
    Olaf

  14. #14
    Lively Member
    Join Date
    Aug 2016
    Posts
    90

    Re: How to get the memory-pointer of a byte-array variable?

    The pointer add for more than 2G/4G is either:
    Code:
    Private Function SafeOffset(thePointer As Long, AmountToAdjust As Long) As Long
        'Lavolpe
        'http://www.vbforums.com/showthread.php?795693-RESOLVED-Pointer-Arithmetic&p=4931477#post4931477
        Dim xAdj As Long
        Const SIGN_BIT = &H80000000
        If AmountToAdjust < 0& Then xAdj = SIGN_BIT
        SafeOffset = ((thePointer Xor SIGN_BIT) + (AmountToAdjust Xor xAdj)) Xor (SIGN_BIT Xor xAdj)
    End Function
    or:
    Code:
    Private Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function

  15. #15

    Thread Starter
    Fanatic Member
    Join Date
    Sep 2012
    Posts
    814

    Re: How to get the memory-pointer of a byte-array variable?

    Thanks for everyone's reply.

    @LaVolpe, the information you provided is very useful, thank you.

    @The trick, the VarPtrVar you wrote works fine. Without your help, my project will not be completed, thank you.

    @DEXWERX, your method is wonderful, and it works fine. When I have time, I'll study your typelib in depth, thank you

    @Elroy, the detailed comments in your source code give me a lot of knowledge. I'm very interested in the Safe-Array and want to know more about it, thank you . By the way, it seems that SafeArrayStruct didn't get the correct array pointer, I don't know why.

    @dilettante, the new PtrOfVValue works fine, you helped me a lot, thank you.

    @Olaf, your source code gives me a deeper understanding of String and Byte-Array. StrOrBytesPtr is very useful, which is concise and clear, thank you.

    @DaveDavis, thank you for your supplementary, the pointer is really a wonderful and powerful tool, thank you.

    As for ByteArray-Pointer, I have another question to ask you experts, I'll post a new thread, thanks again.
    Last edited by dreammanor; Jan 11th, 2018 at 10:06 PM.

  16. #16
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    4,012

    Re: How to get the memory-pointer of a byte-array variable?

    Quote Originally Posted by dreammanor View Post
    @Elroy, the detailed comments in your source code give me a lot of knowledge. I'm very interested in the Safe-Array and want to know more about it, thank you . By the way, it seems that SafeArrayStruct didn't get the correct array pointer, I don't know why.
    Hi Dreammanor,

    I know you've probably moved on, but I wanted to just clarify how my work dovetails in with other approaches. Actually, the pSafeArr is a pointer to the actual SafeArray structure, and not the data. I now realize that you were looking for a pointer to the start of the data in the array.

    From my code, that would be the value returned in the SAFEARRAY0D.pvData variable. That's effectively what The Trick is giving you. His &HC in the "GetMem4 ByVal ptr + &HC, ptr" line is effectively offsetting into the SafeArray structure and grabbing this .pvData value. My code is just a bit more explicit about what it's doing.

    Here's a snippet with both his code and my code in it, for comparison.

    Just throw into Form1:
    Code:
    
    Option Explicit
    Private Declare Function GetMem4 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Dest As Any) As Long
    Private Type SAFEARRAY0D
        cDims As Integer
        fFeatures As Integer
        cbElements As Long
        cLocks As Long
        pvData As Long
    End Type
    Private Type SAFEARRAYBOUND
        cElements As Long
        lLbound As Long
    End Type
    '
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
    '
    
    Private Sub Form_Click()
        Dim a(1 To 5) As Long
    
        Debug.Print VarPtrVar(a)
        MsgBox "Trick's Approach: " & VarPtrVar(a)
    
    
        Dim sa As SAFEARRAY0D
        Dim pSafeArr As Long
        sa = SafeArrayStruct(a, pSafeArr)
    
        Debug.Print sa.pvData
        MsgBox "Elroy's Approach: " & sa.pvData
    
    End Sub
    
    Private Function VarPtrVar(ByRef v As Variant) As Long  ' The Trick's code.
        Dim vt  As Long
        Dim ptr As Long
    
        GetMem4 v, vt
        GetMem4 ByVal VarPtr(v) + 8, ptr
    
        If vt And &H4000 Then GetMem4 ByVal ptr, ptr
    
        If vt And &H2000 Then
            ' // sa
            Dim cDim As Long
    
            GetMem4 ByVal ptr, cDim
    
            If (cDim And &HFFFF&) = 0 Then Exit Function
    
            GetMem4 ByVal ptr + &HC, ptr
    
        End If
    
        VarPtrVar = ptr
    
    End Function
    
    Private Function PtrAdd(ByVal Pointer As Long, ByVal Offset As Long) As Long
        ' For adding (or subtracting) a small number from a pointer.
        ' Use PtrAddEx for adding (or subtracting) large numbers from a pointer.
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Pointer Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function
    
    Private Sub GetSafeArrayInfo(v As Variant, sa As SAFEARRAY0D, sab() As SAFEARRAYBOUND)
        Dim pSafeArr As Long
        '
        sa = SafeArrayStruct(v)
        If sa.cDims = 0 Then Exit Sub ' No dimensions, not dimensioned.
        '
        ReDim sab(0 To sa.cDims - 1)
        '
        ' Now we can get the remainder of the SAFEARRAY structure.
        CopyMemory sab(0), ByVal (pSafeArr + LenB(sa)), CLng(sa.cDims * 8)
    End Sub
    
    Private Function SafeArrayStruct(vArray As Variant, Optional pSafeArr As Long) As SAFEARRAY0D
        ' Just returns if it's not an array.  You should ideally check this before calling here.
        ' It will return the SafeArrayStruct of basically any array.
        ' If passed in, pSafeArr can be used as a return.
        '
        Dim VarType As Integer
        Dim ppSafeArr As Long
        '
        If Not IsArray(vArray) Then Exit Function
        CopyMemory VarType, vArray, 2                                   ' Just get the Variant type from its 16 bytes.
        '
        If VarType And &H4000 Then                                      ' It's a ByRef type Variant array.
            CopyMemory ppSafeArr, ByVal PtrAdd(VarPtr(vArray), 8&), 4&  ' Get pointer to the array inside the Variant structure.
            CopyMemory pSafeArr, ByVal ppSafeArr, 4&                    ' Dereference ppSafeArr.
        Else                                                            ' It's a ByVal type Variant array.
            CopyMemory pSafeArr, ByVal PtrAdd(VarPtr(vArray), 8&), 4&   ' Get pointer to the array inside the Variant structure.
        End If
        If pSafeArr = 0& Then Exit Function
        CopyMemory SafeArrayStruct, ByVal pSafeArr, LenB(SafeArrayStruct)
    End Function
    
    
    Enjoy,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  17. #17

    Thread Starter
    Fanatic Member
    Join Date
    Sep 2012
    Posts
    814

    Re: How to get the memory-pointer of a byte-array variable?

    Quote Originally Posted by Elroy View Post
    Hi Dreammanor,

    I know you've probably moved on, but I wanted to just clarify how my work dovetails in with other approaches. Actually, the pSafeArr is a pointer to the actual SafeArray structure, and not the data. I now realize that you were looking for a pointer to the start of the data in the array.

    From my code, that would be the value returned in the SAFEARRAY0D.pvData variable. That's effectively what The Trick is giving you. His &HC in the "GetMem4 ByVal ptr + &HC, ptr" line is effectively offsetting into the SafeArray structure and grabbing this .pvData value. My code is just a bit more explicit about what it's doing.

    Here's a snippet with both his code and my code in it, for comparison.

    Just throw into Form1:

    ...
    ...

    Enjoy,
    Elroy
    Yes, what I need is the pointer to the start of the data in the array. Now I understand that SAFEARRAY0D.pvData is the value I need. I used safe-array before, but I didn't understand what it meant. Now I fully understand that. Thank you so much, Elroy.
    Last edited by dreammanor; Jan 12th, 2018 at 08:42 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width