63 Stimmen

Wie kann ich feststellen, ob ein Array in VB6 initialisiert ist?

Die Übergabe eines nicht dimensionierten Arrays an die Ubound-Funktion von VB6 führt zu einem Fehler. Ich möchte also überprüfen, ob das Array bereits dimensioniert wurde, bevor ich versuche, seine obere Grenze zu überprüfen. Wie kann ich dies tun?

25voto

GSerg Punkte 73326

Nota: Der Code wurde aktualisiert, die Originalversion finden Sie in der Revisionsgeschichte (nicht dass es nützlich wäre, sie zu finden). Der aktualisierte Code hängt nicht von der undokumentierten GetMem4 Funktion und richtig behandelt Arrays aller Typen.

Hinweis für VBA-Benutzer: Dieser Code ist für VB6, das nie ein x64-Update erhalten hat. Wenn Sie beabsichtigen, diesen Code für VBA zu verwenden, siehe https://stackoverflow.com/a/32539884/11683 für die VBA-Version. Sie müssen nur die CopyMemory Erklärung und die pArrPtr Funktion, der Rest bleibt.

Ich benutze dies:

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(ByRef Destination As Any, ByRef Source As Any, ByVal length As Long)

Private Const VT_BYREF As Long = &H4000&

' When declared in this way, the passed array is wrapped in a Variant/ByRef. It is not copied.
' Returns *SAFEARRAY, not **SAFEARRAY
Public Function pArrPtr(ByRef arr As Variant) As Long
  'VarType lies to you, hiding important differences. Manual VarType here.
  Dim vt As Integer
  CopyMemory ByVal VarPtr(vt), ByVal VarPtr(arr), Len(vt)

  If (vt And vbArray) <> vbArray Then
    Err.Raise 5, , "Variant must contain an array"
  End If

  'see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221627%28v=vs.85%29.aspx
  If (vt And VT_BYREF) = VT_BYREF Then
    'By-ref variant array. Contains **pparray at offset 8
    CopyMemory ByVal VarPtr(pArrPtr), ByVal VarPtr(arr) + 8, Len(pArrPtr)  'pArrPtr = arr->pparray;
    CopyMemory ByVal VarPtr(pArrPtr), ByVal pArrPtr, Len(pArrPtr)          'pArrPtr = *pArrPtr;
  Else
    'Non-by-ref variant array. Contains *parray at offset 8
    CopyMemory ByVal VarPtr(pArrPtr), ByVal VarPtr(arr) + 8, Len(pArrPtr)  'pArrPtr = arr->parray;
  End If
End Function

Public Function ArrayExists(ByRef arr As Variant) As Boolean
  ArrayExists = pArrPtr(arr) <> 0
End Function

Verwendung:

? ArrayExists(someArray)

Ihr Code scheint das Gleiche zu tun (Prüfung auf SAFEARRAY** als NULL), aber auf eine Art und Weise, die ich für einen Compiler-Bug halten würde :)

23voto

raven Punkte 17587

Ich habe gerade an diesen hier gedacht. Einfach genug, keine API-Aufrufe erforderlich. Irgendwelche Probleme damit?

Public Function IsArrayInitialized(arr) As Boolean

  Dim rv As Long

  On Error Resume Next

  rv = UBound(arr)
  IsArrayInitialized = (Err.Number = 0)

End Function

Editar : Ich habe einen Fehler entdeckt, der mit dem Verhalten der Split-Funktion zusammenhängt (eigentlich würde ich es einen Fehler in der Split-Funktion nennen). Nehmen Sie dieses Beispiel:

Dim arr() As String

arr = Split(vbNullString, ",")
Debug.Print UBound(arr)

Wie hoch ist der Wert von Ubound(arr) an dieser Stelle? Er ist -1! Die Übergabe dieses Arrays an die Funktion IsArrayInitialized würde also true zurückgeben, aber der Versuch, auf arr(0) zuzugreifen, würde zu einem Fehler "Subscript out of range" führen.

15voto

raven Punkte 17587

Ich habe mich für das Folgende entschieden. Das ist ähnlich wie das von GSerg Antwort Sie verwendet jedoch die besser dokumentierte API-Funktion CopyMemory und ist völlig eigenständig (Sie können dieser Funktion einfach das Array anstelle von ArrPtr(array) übergeben). Sie verwendet die VarPtr-Funktion, die Microsoft warnt vor aber dies ist eine reine XP-Anwendung und sie funktioniert, also mache ich mir keine Sorgen.

Ja, ich weiß, dass diese Funktion alles akzeptiert, was man ihr hinwirft, aber ich lasse die Fehlerprüfung als Übung für den Leser.

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
  (pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Public Function ArrayIsInitialized(arr) As Boolean

  Dim memVal As Long

  CopyMemory memVal, ByVal VarPtr(arr) + 8, ByVal 4 'get pointer to array
  CopyMemory memVal, ByVal memVal, ByVal 4  'see if it points to an address...  
  ArrayIsInitialized = (memVal <> 0)        '...if it does, array is intialized

End Function

14voto

raven Punkte 17587

Ich habe dies gefunden:

Dim someArray() As Integer

If ((Not someArray) = -1) Then
  Debug.Print "this array is NOT initialized"
End If

Editar : RS Conley betonte in seinem Antwort dass (Not someArray) manchmal 0 zurückgibt, so dass Sie ((Not someArray) = -1) verwenden müssen.

9voto

RS Conley Punkte 7136

Beide Methoden von GSerg und Raven sind undokumentierte Hacks, aber da Visual BASIC 6 nicht mehr entwickelt wird, ist das kein Problem. Das Beispiel von Raven funktioniert jedoch nicht auf allen Rechnern. Sie müssen wie folgt testen.

If (Not someArray) = -1 Then

Auf einigen Rechnern wird eine Null zurückgegeben, auf anderen eine große negative Zahl.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X