|
Für verschiedene Zwecke werden Sie vielleicht einmal ein Array eines beliebigen Datentyps (Integer, Long, Single, Double, Date oder Variant) als Byte-Array benötigen und dann gegebenenfalls von einem Byte-Array wieder in ein Array des ursprünglichen Datentyp zurück verwandeln müssen. Bei Arrays der genannten Datentypen bis auf Variant-Arrays ist das kein großes Problem. Bei Variant-Arrays gilt die Einschränkung, dass es nur Werte mit fester Byte-Länge enthalten darf, also Werte der übrigen genannten Datentypen.
Da die Byte-Längen der verschiedenen Datentypen festgelegt sind und die einzelnen Werte eines Arrays im Speicher hintereinander abgelegt sind, können die Bytes eines Arrays im Speicher direkt an die Stelle der Bytes eines Byte-Arrays kopiert werden. Dies erledigt die API-Funktion RtlMoveMemory, die wir der besseren Merkbarkeit halber unter dem Namen "CopyMemory" deklarieren:
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (Dest As Any, Src As Any, _
ByVal ByteLen As Long)
Ihr wird im ersten Parameter die Speicheradresse des ersten Elements des Ziel-Byte-Arrays übergeben, im zweiten Parameter die Speicheradresse des ersten Elements des zu kopierenden Arrays. Im dritten Parameter ist die Zahl der zu kopierenden Bytes anzugeben. Die Speicheradresse eines Array-Elements liefert Ihnen die (undokumentierte) Visual Basic-Funktion VarPtr.
Der Kopiervorgang sieht nun prinzipiell so aus:
CopyMemory ByVal VarPtr(Bytes(0)), ByVal VarPtr(Arr(0)), AnzahlBytes
Die expliziten ByVal-Angaben können Sie sich sparen, wenn Sie die ByVal-Direktiven gleich in der Deklaration von CopyMemory angeben. Zur Unterscheidung nennen wir die so deklarierte Funktion CopyMemoryV:
Private Declare Sub CopyMemoryV Lib "kernel32" _
Alias "RtlMoveMemory" (ByVal Dest As Long, ByVal Src As Long, _
ByVal ByteLen As Long)
CopyMemoryV VarPtr(Bytes(0)), VarPtr(Arr(0)), AnzahlBytes
Bevor Sie allerdings ein Array in ein Byte-Array kopieren können, müssen Sie dieses Byte-Array zunächst noch auf die erforderliche Größe bringen - es also entsprechend dimensionieren. Die Anzahl der Elemente eines Arrays ist gleich der Differenz zwischen Ober- und Untergrenze des Arrays plus 1. Zur Ermittlung der benötigten Größe des Byte-Arrays und damit auch der Anzahl der zu kopierenden Bytes ist die Anzahl der Elemente des Ausgangs-Arrays noch mit der Byte-Länge eines Elements dieses Arrays zu multiplizieren. Falls Sie beim Ziel-Byte-Array eine andere Untergrenze (Base) als 0 wünschen sollten, ist diese bei der Dimensionierung des Byt-Arrays entsprechend zu berücksichtigen.
Eine Hilfsfunktion, die ein Integer-Array in ein Byte-Array kopiert, sieht somit folgendermaßen aus:
Private Const kIntegerBytes = 2
Public Function IntegerArrToByteArr(Integers() As Integer, _
Optional ByVal Base As Long) As Variant
Dim nLBound As Long
Dim nByteCount As Long
Dim nBytes() As Byte
nLBound = LBound(Integers)
nByteCount = (UBound(Integers) - nLBound + 1) * kIntegerBytes
ReDim nBytes(Base To nByteCount - 1 + Base)
CopyMemoryV VarPtr(nBytes(Base)), VarPtr(Integers(nLBound)), _
nByteCount
IntegerArrToByteArr = nBytes
End Function
Als Rückgabewert der Funktion ist hier absichtlich der Datentyp Variant gewählt - Sie können die Funktion daher gleichermaßen sowohl unter Visual Basic 5 als auch 6 verwenden. Unter VB 6 können Sie den Rückgabewert natürlich auch direkt als Byte-Array deklarieren:
Public Function IntegerArrToByteArr(Integers() As Integer, _
Optional ByVal Base As Long) As Byte()
Die Umkehrung der Funktion, also das Kopieren eines Byte-Arrays in ein Array eines anderes Datentyps beruht auf den gleichen Prinzipien. Sie ist jedoch nur sinnvoll, wenn das Byte-Array auch entsprechende Werte enthält - anderenfalls käme wohl nur Datenmüll zum Vorschein. Das Byte-Array enthält aber keinerlei Informationen darüber, woher seine Bytes stammen und welchen Datentyp sie letztendlich repräsentieren. Das einzige, was Sie sicherheitshalber prüfen können, ist die Anzahl der Bytes - diese muss sich durch die Byte-Länge des Ziel-Arrays teilen lassen (eine Modulo-Division muss den Rest 0 ergeben).
Auch bei der Umkehrfunktion können Sie wieder die Untergrenze (Base) des Ziel-Arrays festlegen. Die VB 6-Variante, die direkt ein Integer-Array zurückgibt, sieht so aus:
Public Function ByteArrToIntegerArr(Bytes() As Byte, _
Optional ByVal Base As Long) As Integer()
Dim nLBound As Long
Dim nByteCount As Long
Dim nIntegers() As Integer
nLBound = LBound(Bytes)
nByteCount = (UBound(Bytes) - nLBound + 1)
If nByteCount Mod kIntegerBytes <> 0 Then
Err.Raise kErrInvalidBytesCount
Else
ReDim nIntegers(Base To _
(nByteCount \ kIntegerBytes) - 1 + Base)
CopyMemoryV _
VarPtr(nIntegers(Base)), VarPtr(Bytes(nLBound)), nByteCount
ByteArrToIntegerArr = nIntegers
End If
End Function
Da unter VB 5 nur Byte-Arrays als Rückgabewert einer Funktion übergeben werden können, muss die Umkehrfunktion hier als Prozedur mit einem zusätzlichen Parameter zur Übergabe des Ziel-Arrays angelegt werden:
Public Sub ByteArrToIntegerArr(Bytes() As Byte, _
Integers() As Integer, Optional ByVal Base As Long)
Dim nLBound As Long
Dim nByteCount As Long
nLBound = LBound(Bytes)
nByteCount = (UBound(Bytes) - nLBound + 1)
If nByteCount Mod kIntegerBytes <> 0 Then
Err.Raise kErrInvalidBytesCount
Else
ReDim Integers(Base To _
(nByteCount \ kIntegerBytes) - 1 + Base)
CopyMemoryV _
VarPtr(Integers(Base)), VarPtr(Bytes(nLBound)), nByteCount
End If
End Sub
Den Code der entsprechenden Funktionen für die Datentypen Long, Single, Double, Date und Variant finden Sie in den Modulen, die Sie zu diesem Artikel herunterladen können.
Auch Arrays einer benutzerdefinierten Variablen (Type) können in ein Byte-Array kopiert werden. Als Elemente einer solchen Variablen sind auch Strings mit fester Länge als auch wiederum Arrays und sogar Verschachtelungen von benutzerdefinierten Variablen zulässig. Voraussetzung ist immer, dass letztlich alle Elemente eine feste Byte-Länge aufweisen. Damit Sie die Byte-Länge einer solchen benutzerdefinierten Variablen nicht selbst nachzählen müssen (wie leicht verzählt man sich dabei...), können Sie sie dynamisch über die VB-Funktion LenB ermitteln, der Sie eine hilfsweise deklarierte einzelne Variable des benutzerdefinierten Typs übergeben.
Public Type SampleType
Byte As Byte
Integer As Integer
Long As Long
Single As Single
Double As Double
Date As Date
Variant As Variant
String As String * 1
End Type
Public Function SampleTypeArrToByteArr(SampleTypes() As SampleType, _
Optional ByVal Base As Long) As Byte()
Dim nLBound As Long
Dim nByteCount As Long
Dim nBytes() As Byte
Dim nSampleTypeBytes As Long
Dim nSampleType As SampleType
nSampleTypeBytes = LenB(nSampleType)
nLBound = LBound(SampleTypes)
nByteCount = (UBound(SampleTypes) - nLBound + 1) _
* nSampleTypeBytes
ReDim nBytes(Base To nByteCount - 1 + Base)
CopyMemoryV VarPtr(nBytes(Base)), VarPtr(SampleTypes(nLBound)), _
nByteCount
SampleTypeArrToByteArr = nBytes
End Function
Hier wieder die abweichende Deklaration der Funktion für VB 5:
Public Function SampleTypeArrToByteArr(SampleTypes() As SampleType, _
Optional ByVal Base As Long) As Byte()
Und die Gegenstücke, hier für VB 6:
Public Function ByteArrToSampleTypeArr(Bytes() As Byte, _
Optional ByVal Base As Long) As SampleType()
Dim nLBound As Long
Dim nByteCount As Long
Dim nSampleTypes() As SampleType
Dim nSampleTypeBytes As Long
Dim nSampleType As SampleType
nSampleTypeBytes = LenB(nSampleType)
nLBound = LBound(Bytes)
nByteCount = (UBound(Bytes) - nLBound + 1)
If nByteCount Mod nSampleTypeBytes <> 0 Then
Err.Raise kErrInvalidBytesCount
Else
ReDim nSampleTypes(Base To _
(nByteCount \ nSampleTypeBytes) - 1 + Base)
CopyMemoryV VarPtr(nSampleTypes(Base)), _
VarPtr(Bytes(nLBound)), nByteCount
ByteArrToSampleTypeArr = nSampleTypes
End If
End Function
und für VB 5:
Public Sub ByteArrToSampleTypeArr(Bytes() As Byte, _
SampleType() As SampleType, Optional ByVal Base As Long)
Dim nLBound As Long
Dim nByteCount As Long
Dim nSampleTypeBytes As Long
Dim nSampleType As SampleType
nSampleTypeBytes = LenB(nSampleType)
nLBound = LBound(Bytes)
nByteCount = (UBound(Bytes) - nLBound + 1)
If nByteCount Mod nSampleTypeBytes <> 0 Then
Err.Raise kErrInvalidBytesCount
Else
ReDim SampleTypes(Base To _
(nByteCount \ nSampleTypeBytes) - 1 + Base)
CopyMemoryV _
VarPtr(SampleTypes(Base)), VarPtr(Bytes(nLBound)), nByteCount
End If
End Sub
|