Results 1 to 4 of 4

Thread: This allows you to convert any normal array into a byte array.

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Oct 2008
    Posts
    1,181

    This allows you to convert any normal array into a byte array.

    It uses a Variant for the input parameter so that you don't need to have a specific array type (Byte, Integer, etc) when putting the array into the parameter. It then uses API functions, rather than VB6 functions, for handling the SafeArray so as to be usable regardless of the data type of the array, regardless of the number of dimensions, and regardless of the lower bounds of the dimensions. It copies the entire content of the array (as long as it's a fairly normal type, not something with variable length entries like an array of strings or variants) to a byte array. This is very useful if you want to treat the array as a single chunk of data, such as for input to various functions that act on a single large piece of data. These might be checksum, CRC, or hash type functions, or even an encryption function. This should work with any arrays of any of the numeric data types (Byte, Integer, Long, Currency, Single, or Double).

    Below is the code for this function, as well as the declare statements that you will need to make it work.
    Code:
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
    Private Declare Sub SafeArrayAccessData Lib "oleaut32.dll" (ByVal psa As Long, ByRef ppvData As Any)
    Private Declare Sub SafeArrayUnaccessData Lib "oleaut32.dll" (ByVal psa As Long)
    Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByVal psa As Long) As Long
    Private Declare Function SafeArrayGetElemsize Lib "oleaut32.dll" (ByVal psa As Long) As Long
    
    
    
    Private Function AnyArrayToBytes(ByVal SafeArray As Variant) As Byte()
    Dim SArrayPtr As Long
    Dim ElemSize As Long
    Dim DimCount As Long
    Dim ElemsInDim As Long
    Dim TotalElems As Long
    Dim DataSize As Long
    Dim DataPtr As Long
    Dim Bytes() As Byte
    Dim n As Long
    
    CopyMemory SArrayPtr, ByVal VarPtr(SafeArray) + 8, 4
    If SArrayPtr = 0 Then Exit Function
    DimCount = SafeArrayGetDim(SArrayPtr)
    ElemSize = SafeArrayGetElemsize(SArrayPtr)
    
    TotalElems = 1
    For n = 0 To DimCount - 1
        CopyMemory ElemsInDim, ByVal SArrayPtr + 16 + n * 8, 4
        TotalElems = TotalElems * ElemsInDim
    Next n
    
    DataSize = TotalElems * ElemSize
    ReDim Bytes(DataSize - 1)
    SafeArrayAccessData SArrayPtr, DataPtr
    CopyMemory Bytes(0), ByVal DataPtr, DataSize
    SafeArrayUnaccessData SArrayPtr
    
    AnyArrayToBytes = Bytes()
    End Function


    Here's some code to test it out. Make sure that your Form1 has the property AutoRedraw set to True.
    Code:
    Private Sub Form_Load()
    Dim a(3, 1) As Currency
    Dim b() As Byte
    
    b() = AnyArrayToBytes(a)
    Print UBound(b)
    End Sub

    The value printed on Form1 should be 63.
    Here's why. There are 2 dimensions. The upper bounds are 3 and 1. The lower bounds are both 0. So this is a size of 4 in the first dimension, and 2 in the second dimension. This makes 8 elements. Since each element is of Currency type, which occupies 8 bytes, this gives 8*8=64 bytes. Since the byte array returned from the function has 0 as the lower bound, the upper bound is 63.
    Last edited by Ben321; Feb 7th, 2016 at 02:06 AM.

  2. #2
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: This allows you to convert any normal array into a byte array.

    Tip: If one does not want to copy the data, but just wants to reference it a different way, one can simply overlay a SafeArray structure on an uninitialized array() variable. Remove overlay when done with it. For example, you have a 2D array of Longs and you want to access it as a 1D array of Longs or 1D array of bytes. The number of dimensions are not limited, the LBound can be changed in the overlay to anything.

    Advantage: no copying of any data. Both arrays, the original and overlay, point to the same exact data. Make a change in one, the change is in the other. In your example, the passed data is actually being copied twice: once due to parameter ByVal in AnyArrayToBytes and again during the processing.

    Disadvantage: unsafe if sloppy coding. The overlay must be removed at some point before the original data is destroyed else crash as VB tries to free the array memory twice. But this disadvantage is trivial for seasoned coders.

    Edited: If you'd like to reduce at least one level of copying data, you may want to pass the array ByRef vs. ByVal. If you do then the 1st 2 bytes of the variant parameter will contain VT_BYREF (&H4000&). That means the value at variant offset of 8 is a pointer to a pointer to a SafeArray and the source data SafeArray can be accessed from there

    Using a simple test, this can be validated:
    Code:
    Private Sub TestArrayPointers(ByVal inArray As Variant)
    
        Const VT_BYREF As Long = &H4000&                                ' Variant type
        Dim aVal As Long, aData As Long
        CopyMemory aVal, ByVal VarPtr(inArray), 2&                      ' get variant type
        If (aVal And vbArray) = 0& Then Exit Sub                        ' what was passed is not an array
        
        CopyMemory aData, ByVal VarPtr(inArray) + 8&, 4&                ' get pointer to SafeArray structure
        If (aVal And VT_BYREF) = VT_BYREF Then                          ' if array is in Variant ByRef then...
            If aData Then CopyMemory aData, ByVal aData, 4&             ' get pointer to SafeArray structure
        End If
        If aData = 0& Then Exit Sub                                     ' uninitialized array
        
        ' Testing code follows: print out the SafeArray pvData member
        CopyMemory aVal, ByVal aData + 12&, 4&
        Debug.Print "VarPtr of 1st array element is: " & aVal
    End Sub
    Call the above routine twice, once using ByVal for the parameter and again after changing it to ByRef. Example of a call
    Code:
    Private Sub Command1_Click() 
        Dim b(0 to 1) As Byte
        Debug.Print "VarPtr of 1st array element is: "; VarPtr(b(0))
        TestArrayPointers b()
    End Function
    Last edited by LaVolpe; Feb 7th, 2016 at 10:57 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}

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Oct 2008
    Posts
    1,181

    Re: This allows you to convert any normal array into a byte array.

    Quote Originally Posted by LaVolpe View Post
    Tip: If one does not want to copy the data, but just wants to reference it a different way, one can simply overlay a SafeArray structure on an uninitialized array() variable. Remove overlay when done with it. For example, you have a 2D array of Longs and you want to access it as a 1D array of Longs or 1D array of bytes. The number of dimensions are not limited, the LBound can be changed in the overlay to anything.

    Advantage: no copying of any data. Both arrays, the original and overlay, point to the same exact data. Make a change in one, the change is in the other. In your example, the passed data is actually being copied twice: once due to parameter ByVal in AnyArrayToBytes and again during the processing.

    Disadvantage: unsafe if sloppy coding. The overlay must be removed at some point before the original data is destroyed else crash as VB tries to free the array memory twice. But this disadvantage is trivial for seasoned coders.

    Edited: If you'd like to reduce at least one level of copying data, you may want to pass the array ByRef vs. ByVal. If you do then the 1st 2 bytes of the variant parameter will contain VT_BYREF (&H4000&). That means the value at variant offset of 8 is a pointer to a pointer to a SafeArray and the source data SafeArray can be accessed from there

    Using a simple test, this can be validated:
    Code:
    Private Sub TestArrayPointers(ByVal inArray As Variant)
    
        Const VT_BYREF As Long = &H4000&                                ' Variant type
        Dim aVal As Long, aData As Long
        CopyMemory aVal, ByVal VarPtr(inArray), 2&                      ' get variant type
        If (aVal And vbArray) = 0& Then Exit Sub                        ' what was passed is not an array
        
        CopyMemory aData, ByVal VarPtr(inArray) + 8&, 4&                ' get pointer to SafeArray structure
        If (aVal And VT_BYREF) = VT_BYREF Then                          ' if array is in Variant ByRef then...
            If aData Then CopyMemory aData, ByVal aData, 4&             ' get pointer to SafeArray structure
        End If
        If aData = 0& Then Exit Sub                                     ' uninitialized array
        
        ' Testing code follows: print out the SafeArray pvData member
        CopyMemory aVal, ByVal aData + 12&, 4&
        Debug.Print "VarPtr of 1st array element is: " & aVal
    End Sub
    Call the above routine twice, once using ByVal for the parameter and again after changing it to ByRef. Example of a call
    Code:
    Private Sub Command1_Click() 
        Dim b(0 to 1) As Byte
        Debug.Print "VarPtr of 1st array element is: "; VarPtr(b(0))
        TestArrayPointers b()
    End Function


    In the case I used the code that I wrote, when data needed to be input as any number of different possible types of arrays, but the output format didn't matter, as it was simply going to be saved to a file, but needed to be for sure in byte array format for another process prior to saving (such calculating the CRC of the data, which would then also be saved to the file).

  4. #4
    Member
    Join Date
    May 2013
    Posts
    47

    Re: This allows you to convert any normal array into a byte array.

    Private Sub Form_Load()
    Dim a(2), b() As Byte

    a(0) = 51: a(1) = 53: a(2) = 54
    b = AnyArrayToBytes(a)
    End Sub

    if you have some ider change a to b as byte array.

Posting Permissions

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



Click Here to Expand Forum to Full Width