Visual Basic lässt leider eine elementare Grundfunktionalität im Hinblick auf Arrays vermissen: die Möglichkeit, leere Arrays zu deklarieren. Zwar können Sie mit Dim arLng() As Long ein dynamisches Array deklarieren und später redimensionieren, wie es Ihnen gefällt. Wenn Sie jedoch die Arraygrenzen eines derartigen definierten Datenfeldes vor jeglicher Redimensionierung zum ersten Mal abfragen möchten, so werden Sie kläglich scheitern und mit dem Laufzeitfehler 9 „Index außerhalb des gültigen Bereichs“ bestraft. Besonders ärgerlich wird dies bei dynamischen Änderungen des Arrays in einer Schleife der Art For i = 0 To n ReDim Preserve arLng(UBound(arLng) + 1) arLng(UBound(arLng)) = anyValue Next i Da das Array zum Eintritt der Schleife noch uninitialisiert sein kann, müssen Sie irgendeine Abfrage innerhalb der Schleife einbauen, die überprüft, ob das Array initialisiert ist: For i = 0 To n ' Testen, ob Array dimensioniert ist If (Not Not arLng) <> 0 Then ReDim Preserve arLng(UBound(arLng) + 1) Else ' Array ist nicht dimensioniert -> ' Array mit einem Element dimensionieren ReDim arLng(0) End If arLng(UBound(arLng)) = anyValue Next i Selbst für den Fall, dass die Schleife nicht Bestandteil eines zeitkritischen Codeabschnitts ist, kann die obige Ausführung nicht eben elegant genannt werden und ist einzig dem unverständlichen Umstand zu verdanken, dass in VB keine leeren Arrays deklariert werden können. Um die Aufgabe so einfach wie möglich zu halten, wollen wir uns auf eindimensionale Arrays beschränken.
Wir müssen nach einem Weg suchen, der Prozedur möglichst viele Arraytypen zu übergeben. Der Prozedur soll es mithin egal sein, ob es sich um ein Array aus Long-, Integer-, String oder gar selbstdefinierten Variablen handelt. Der einzige Weg dies zu tun, ist der Prozedur das Array als Variant-Variable ByRef zu übergeben. Da wir das Datenfeld als Referenz an die Prozedur weiterreichen, haben wir den Rückgabewert der Funktion frei zu unserer Verfügung. Daher wählen wir einen booleschen Rückgabewert, der uns anzeigt, ob die Redimensionierung erfolgreich oder nicht gewesen ist. Da wir einen Variant-Wert als Parameter übergeben, sollte gleich am Anfang der Funktion geprüft werden, ob es sich bei diesem Wert auch tatsächlich um ein Array handelt. Schließlich kann sich hinter der Variant-Variable alles Mögliche verbergen. Ein VB-Array ist nichts anderes als die Kapselung eines sog. SafeArrays, dessen Struktur in der Bibliothek "oleaut32.dll“ definiert ist. Genauer gesagt ist eine VB-Array-Variable ein Zeiger, der auf einen weiteren Zeiger verweist, der seinerseits angibt, an welcher Stelle im Speicher der sog. SelfArray-Descriptor abgelegt ist. Dieser Deskriptor beschreibt den Aufbau der eigentlichen Arraydaten, u.a. die Untergrenze des SafeArrays, die Anzahl seiner Elemente und somit implizit auch die obere Grenze des Arrays sowie den Speicherort der Arrayelemente. Für unsere Funktion müssen wir Daten aus dem Speicher lesen können. Dafür können wir entweder die Api-Funktion CopyMemory oder die in der VB Laufzeitbibliothek "msvbvm60.dll" deklariertren Prozeduren GetMem2 und GetMem4 verwenden. Da ich persönlich GetMemX übersichtlicher finde, werde ich auf diese Funktionen zurückgreifen. Wenn es Ihnen jedoch mehr Spaß macht, können Sie natürlich auch CopyMemory einsetzen und jedes Mal neu ausbaldowern, ob Sie jetzt etwas ByRef oder ByVal übergeben müssen. Code in einem Modul: Option Explicit Private Const VT_BYREF% = 16384 ' Private Type SAFEARRAYBOUND cElements As Long lLbound As Long End Type Private Declare Sub GetMemLong Lib "msvbvm60" _ Alias "GetMem4" ( _ ByVal Addr As Long, _ RetVal As Long) Private Declare Sub GetMemInt Lib "msvbvm60" _ Alias "GetMem2" ( _ ByVal Addr As Long, _ RetVal As Integer) Private Declare Function SafeArrayRedim Lib "oleaut32.dll" ( _ ByRef pSa As Long, _ ByRef psaBoundNew As SAFEARRAYBOUND) As Long Public Function EmptyArray(ByRef varArray As Variant, _ Optional loBound& = 0) As Boolean ' Testen, ob in varArray übergebener Parameter auch wirklich ' ein Array ist If Not IsArray(varArray) Then Exit Function Dim pVariant As Long ' Pointer auf die übergebene Variant-Variable Dim vType As Integer ' Datentyp der Variant-Variable Dim pSa As Long ' Pointer auf SafeArray-Struktur Dim pFarSa As Long ' Pointer auf Pointer auf SafeArray-Struktur ' Redimensionieren des Arrays auf die übliche Art. ' Dies ist nötig, weil ein undimensioniertes Array ' später einen Nullzeiger liefern würde ReDim varArray(0) ' Speicheradresse der Variant-Variable holen pVariant = VarPtr(varArray) ' den Datentyp der Variant-Variable holen ' er steht direkt am Anfang (ersten zwei Bytes) ' CopyMemory vType, ByVal pVariant, 2 GetMemInt pVariant, vType ' Prüfen, ob es sich um eine VT_BYREF-Variant-Variable handelt If vType And VT_BYREF Then ' Array ist VT_BYREF, also steckt in der ' Variant-Struktur ein Pointer auf den ' Pointer zum SaveArray-Deskriptor GetMemLong pVariant + 8, pFarSa GetMemLong pFarSa, pSa Else ' nicht VT_BYREF, also steckt der Pointer ' auf die SafeArray-Struktur ' direkt in der Variant-Struktur GetMemLong pVariant + 8, pSa End If ' Wird der Funktion ein undimensioniertes Array übergeben ' und hätten wir das Array nicht eingangs mit ' ReDim varArray(0) redimensioniert, wäre pSa jetzt gleich Null. ' Wir stellen dennoch sicher, dass pSa ungleich Null ist If pSa = 0 Then Erase varArray EmptyArray = False Exit Function End If ' nun wird das Array mit Null Elementen ' dimensioniert und die Untergrenze auf ' loBound festgelegt Dim saBound As SAFEARRAYBOUND saBound.lLbound = loBound Dim ret& ret = SafeArrayRedim(ByVal pSa, saBound) EmptyArray = (ret = 0) End Function Hier noch eine Testprozedur: Private Sub Command1_Click() Dim arStr() As String Dim arLng() As Long Dim arCol() As Collection ' benutzen Sie für arUdt einen selbstdefinerten ' Datentyp aus einer Klassenbibliothek ihrer Wahl ' Dim arUdt() As MsrPairType Dim arVar() As Variant EmptyArray arStr Debug.Print "Unter- und Obergrenze StringArray " & LBound(arStr) & " " & UBound(arStr) ReDim arStr(44) Debug.Print "Unter- und Obergrenze StringArray normalem ReDim " & LBound(arStr) & " " & UBound(arStr) EmptyArray arStr Debug.Print "Unter- und Obergrenze StringArray nach erneutem EmptyArray " & LBound(arStr) & " " & UBound(arStr) EmptyArray arLng Debug.Print "Unter- und Obergrenze Long-Array " & LBound(arLng) & " " & UBound(arLng) ReDim arLng(-12 To 8) Debug.Print "Unter- und Obergrenze Long-Array nach ReDim" & LBound(arLng) & " " & UBound(arLng) EmptyArray arLng, -100 Debug.Print "Unter- und Obergrenze Long-Array nach erneutem EmptyArray " & LBound(arLng) & " " & UBound(arLng) EmptyArray arCol, -3 Debug.Print "Unter- und Obergrenze Objekt-Array " & LBound(arCol) & " " & UBound(arCol) ' EmptyArray arUdt, 33 ' Debug.Print "Unter- und Obergrenze UDT-Array " & LBound(arUdt) & " " & UBound(arUdt) EmptyArray arVar Debug.Print "Unter- und Obergrenze Variant-Array " & LBound(arVar) & " " & UBound(arVar) End Sub Abschließend soll noch gesagt sein, dass die Funktion ein guter Kandidat für eine separate Klasse mit der Instancing-Eigenschaft GlobalMutliUse ist. Für diesen Fall müssten wir natürlich noch eine Fehlerbehandlung einbauen. Besonders fehlgeschlagene Aufrufe von SafeArrayRedim sollten behandelt und dem User als klar lesbare Fehlermeldung angezeigt werden. Dieser Tipp wurde bereits 62.723 mal aufgerufen. Voriger Tipp | Zufälliger Tipp | Nächster Tipp
Anzeige
Diesen und auch alle anderen Tipps & Tricks finden Sie auch auf unserer aktuellen vb@rchiv Vol.6 (einschl. Beispielprojekt!) Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv! - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m. |
Neu! sevDTA 3.0 Pro SEPA mit Kontonummernprüfung Erstellen von SEPA-Dateien mit integriertem BIC-Verzeichnis und Konto- nummern-Prüfverfahren, so dass ungültige Bankdaten bereits im Vorfeld ermittelt werden können. Tipp des Monats März 2024 Dieter Otter UTF-8 Konvertierung von Dateien und Strings VB6 selbst verfügt über keine Funktionen zur UTF-8 Konvertierung von Daten. Mit Hilfe des ADODB.Stream-Objekts lassen sich diese fehlenden Funktionen aber schnell nachrüsten. TOP Entwickler-Paket TOP-Preis!! Mit der Developer CD erhalten Sie insgesamt 24 Entwickler- komponenten und Windows-DLLs. Die Einzelkomponenten haben einen Gesamtwert von 1605.50 EUR... |
||||||||||||||||
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein. |