-
Feb 7th, 2016, 01:59 AM
#1
Thread Starter
Frenzied Member
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.
-
Feb 7th, 2016, 10:07 AM
#2
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.
-
Feb 9th, 2016, 12:59 AM
#3
Thread Starter
Frenzied Member
Re: This allows you to convert any normal array into a byte array.
Originally Posted by LaVolpe
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).
-
Feb 16th, 2016, 11:40 PM
#4
Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|