vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
SEPA-Dateien erstellen inkl. IBAN-, BLZ-/Kontonummernprüfung  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2019
 
zurück
Rubrik: Variablen/Strings · Arrays   |   VB-Versionen: VB622.02.05
Arrays leer initialisieren

Mit VB6 können Sie keine leeren Arrays deklarieren. Die Funktion EmptyArray leistet Abhilfe.

Autor:   Andreas HaackBewertung:     [ Jetzt bewerten ]Views:  57.281 
ohne HomepageSystem:  Win9x, WinNT, Win2k, WinXP, Vista, Win7, Win8, Win10 Beispielprojekt auf CD 

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.
Es wäre also wünschenswert eine Funktion „EmptyArray“ zur Verfügung zu haben, die uns ein Datenfeld mit Null Elementen zurückgibt, das dann mit LBound oder UBound angesprochen werden kann.

Um die Aufgabe so einfach wie möglich zu halten, wollen wir uns auf eindimensionale Arrays beschränken.
Unsere Funktion soll

  1. ein eindimensionales Array entgegennehmen
  2. dieses so redimensionieren, dass das Array danach Null Elemente enthält
  3. die Untergrenze des Arrays beliebig festsetzen können
  4. mit Lbound bzw. Ubound abgefragt werden können.

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.
"Oleaut32.dll" exportiert des weiteren die Funktion SafeArrayRedim, mit der ein Datenfeld beliebig neu dimensioniert werden kann. Mit ihrer Hilfe wird es uns gelingen, das Array wie gewünscht zu manipulieren. Unter VB6 importieren wir die Funktion wie gewohnt mit der Declare-Anweisung:

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.
Die Funktion sollte besser auch überprüfen, ob tatsächlich ein eindimensionales Array übergeben wurde.
 

Dieser Tipp wurde bereits 57.281 mal aufgerufen.

Voriger Tipp   |   Zufälliger Tipp   |   Nächster Tipp

Über diesen Tipp im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Tipp, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Aktuelle Diskussion anzeigen (2 Beiträge)

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!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.
 
   

Druckansicht Druckansicht Copyright ©2000-2019 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
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.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel