How do I determine if an array is initialized in VB6?

ArraysVb6

Arrays Problem Overview


Passing an undimensioned array to the VB6's Ubound function will cause an error, so I want to check if it has been dimensioned yet before attempting to check its upper bound. How do I do this?

Arrays Solutions


Solution 1 - Arrays

> Note: the code has been updated, the original version can be found in the revision history (not that it is useful to find it). The updated code does not depend on the undocumented GetMem4 function and correctly handles arrays of all types.

> Note for VBA users: This code is for VB6 which never got an x64 update. If you intend to use this code for VBA, see https://stackoverflow.com/a/32539884/11683 for the VBA version. You will only need to take the CopyMemory declaration and the pArrPtr function, leaving the rest.

I use this:

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

Usage:

? ArrayExists(someArray)

Your code seems to do the same (testing for SAFEARRAY** being NULL), but in a way which I would consider a compiler bug :)

Solution 2 - Arrays

I just thought of this one. Simple enough, no API calls needed. Any problems with it?

Public Function IsArrayInitialized(arr) As Boolean

  Dim rv As Long
  
  On Error Resume Next
  
  rv = UBound(arr)
  IsArrayInitialized = (Err.Number = 0)

End Function

Edit: I did discover a flaw with this related to the behavior of the Split function (actually I'd call it a flaw in the Split function). Take this example:

Dim arr() As String

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

What is the value of Ubound(arr) at this point? It's -1! So, passing this array to this IsArrayInitialized function would return true, but attempting to access arr(0) would cause a subscript out of range error.

Solution 3 - Arrays

Here's what I went with. This is similar to GSerg's answer, but uses the better documented CopyMemory API function and is entirely self-contained (you can just pass the array rather than ArrPtr(array) to this function). It does use the VarPtr function, which Microsoft warns against, but this is an XP-only app, and it works, so I'm not concerned.

Yes, I know this function will accept anything you throw at it, but I'll leave the error checking as an exercise for the reader.

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

Solution 4 - Arrays

I found this:

Dim someArray() As Integer

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

Edit: RS Conley pointed out in his answer that (Not someArray) will sometimes return 0, so you have to use ((Not someArray) = -1).

Solution 5 - Arrays

Both methods by GSerg and Raven are undocumented hacks but since Visual BASIC 6 is no longer being developed then it is not a issue. However Raven's example doesn't work on all machines. You have to test like this.

If (Not someArray) = -1 Then

On some machines it will return a zero on others some large negative number.

Solution 6 - Arrays

In VB6 there is a function called "IsArray", but it does not check if the array has been initialized. You will receive Error 9 - Subscript out of range if you attempt to use UBound on an uninitialized array. My method is very similar to S J's, except it works with all variable types and has error handling. If a non-array variable is checked, you will receive Error 13 - Type Mismatch.

Private Function IsArray(vTemp As Variant) As Boolean
    On Error GoTo ProcError
    Dim lTmp As Long
    
    lTmp = UBound(vTemp) ' Error would occur here
    
    IsArray = True: Exit Function
ProcError:
    'If error is something other than "Subscript
    'out of range", then display the error
    If Not Err.Number = 9 Then Err.Raise (Err.Number)
End Function

Solution 7 - Arrays

Since wanted comment on here will post answer.

Correct answer seems is from @raven:

Dim someArray() As Integer

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

When documentation or Google does not immediately return an explanation people tend to call it a hack. Although what seems to be the explanation is that Not is not only a Logical, it is also a Bitwise operator, so it handles the bit representation of structures, rather than Booleans only.

For example of another bitwise operation is here:

Dim x As Integer
x = 3 And 5 'x=1

So the above And is also being treated as a bitwise operator.

Furthermore, and worth to check, even if not the directly related with this, > The Not operator can be overloaded, which means that a class or > structure can redefine its behavior when its operand has the type of > that class or structure. Overloading

Accordingly, Not is interpreting the array as its bitwise representation and it distinguishes output when array is empty or not like differently in the form of signed number. So it can be considered this is not a hack, is just an undocumentation of the array bitwise representation, which Not here is exposing and taking advantage of.

> Not takes a single operand and inverts all the bits, including the > sign bit, and assigns that value to the result. This means that for > signed positive numbers, Not always returns a negative value, and for > negative numbers, Not always returns a positive or zero value. Logical Bitwise

Having decided to post since this offered a new approach which is welcome to be expanded, completed or adjusted by anyone who has access to how arrays are being represented in their structure. So if anyone offers proof it is actually not intended for arrays to be treated by Not bitwise we should accept it as not a hack and actually as best clean answer, if they do or do not offer any support for this theory, if it is constructive comment on this is welcome of course.

Solution 8 - Arrays

This is modification of raven's answer. Without using API's.

Public Function IsArrayInitalized(ByRef arr() As String) As Boolean
'Return True if array is initalized
On Error GoTo errHandler 'Raise error if directory doesnot exist
  
  Dim temp As Long
  temp = UBound(arr)
  
  'Reach this point only if arr is initalized i.e. no error occured
  If temp > -1 Then IsArrayInitalized = True 'UBound is greater then -1

Exit Function
errHandler:
  'if an error occurs, this function returns False. i.e. array not initialized
End Function

This one should also be working in case of split function. Limitation is you would need to define type of array (string in this example).

Solution 9 - Arrays

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (arr() As Any) As Long

Private Type SafeArray
    cDims As Integer
    fFeatures As Integer
    cbElements As Long
    cLocks As Long
    pvData As Long
End Type
  
Private Function ArrayInitialized(ByVal arrayPointer As Long) As Boolean
    Dim pSafeArray As Long
    
    CopyMemory pSafeArray, ByVal arrayPointer, 4
    
    Dim tArrayDescriptor As SafeArray
    
    If pSafeArray Then
        CopyMemory tArrayDescriptor, ByVal pSafeArray, LenB(tArrayDescriptor)
        
        If tArrayDescriptor.cDims > 0 Then ArrayInitialized = True
    End If

End Function

Usage:

Private Type tUDT
    t As Long
End Type

Private Sub Form_Load()
    Dim longArrayNotDimmed() As Long
    Dim longArrayDimmed(1) As Long
    
    Dim stringArrayNotDimmed() As String
    Dim stringArrayDimmed(1) As String
    
    Dim udtArrayNotDimmed() As tUDT
    Dim udtArrayDimmed(1) As tUDT
    
    Dim objArrayNotDimmed() As Collection
    Dim objArrayDimmed(1) As Collection
    
    
    Debug.Print "longArrayNotDimmed " & ArrayInitialized(ArrPtr(longArrayNotDimmed))
    Debug.Print "longArrayDimmed " & ArrayInitialized(ArrPtr(longArrayDimmed))
    
    Debug.Print "stringArrayNotDimmed " & ArrayInitialized(ArrPtr(stringArrayNotDimmed))
    Debug.Print "stringArrayDimmed " & ArrayInitialized(ArrPtr(stringArrayDimmed))
    
    Debug.Print "udtArrayNotDimmed " & ArrayInitialized(ArrPtr(udtArrayNotDimmed))
    Debug.Print "udtArrayDimmed " & ArrayInitialized(ArrPtr(udtArrayDimmed))
    
    Debug.Print "objArrayNotDimmed " & ArrayInitialized(ArrPtr(objArrayNotDimmed))
    Debug.Print "objArrayDimmed " & ArrayInitialized(ArrPtr(objArrayDimmed))

    Unload Me
End Sub

Solution 10 - Arrays

When you initialite the array put an integer or boolean with a flag = 1. and query this flag when you need.

Solution 11 - Arrays

Based on all the information I read in this existing post this works the best for me when dealing with a typed array that starts as uninitialized.

It keeps the testing code consistent with the usage of UBOUND and It does not require the usage of error handling for testing.

It IS dependent on Zero Based Arrays (which is the case in most development).

Must not use "Erase" to clear the array. use alternative listed below.

Dim data() as string ' creates the untestable holder.
data = Split(vbNullString, ",") ' causes array to return ubound(data) = -1
If Ubound(data)=-1 then ' has no contents
    ' do something
End If
redim preserve data(Ubound(data)+1) ' works to increase array size regardless of it being empty or not.

data = Split(vbNullString, ",") ' MUST use this to clear the array again.

Solution 12 - Arrays

The easiest way to handle this is to insure that the array is initialized up front, before you need to check for the Ubound. I needed an array that was declared in the (General) area of the form code. i.e.

Dim arySomeArray() As sometype

Then in the form load routine I redim the array:

Private Sub Form_Load()

ReDim arySomeArray(1) As sometype 'insure that the array is initialized

End Sub 

This will allow the array to be re-defined at any point later in the program. When you find out how big the array needs to be just redim it.

ReDim arySomeArray(i) As sometype 'i is the size needed to hold the new data

Solution 13 - Arrays

For any variable declared as an array, you can easily check if the array is initialized by calling the SafeArrayGetDim API. If the array is initialized, then the return value will be non-zero, otherwise the function returns zero.

Note that you can't use this function with variants that contain arrays. Doing so will cause a Compile error (Type mismatch).

Public Declare Function SafeArrayGetDim Lib "oleaut32.dll" (psa() As Any) As Long

Public Sub Main()
    Dim MyArray() As String

    Debug.Print SafeArrayGetDim(MyArray)    ' zero

    ReDim MyArray(64)
    Debug.Print SafeArrayGetDim(MyArray)    ' non-zero

    Erase MyArray
    Debug.Print SafeArrayGetDim(MyArray)    ' zero

    ReDim MyArray(31, 15, 63)
    Debug.Print SafeArrayGetDim(MyArray)    ' non-zero

    Erase MyArray
    Debug.Print SafeArrayGetDim(MyArray)    ' zero

    ReDim MyArray(127)
    Debug.Print SafeArrayGetDim(MyArray)    ' non-zero

    Dim vArray As Variant
    vArray = MyArray
    ' If you uncomment the next line, the program won't compile or run.
    'Debug.Print SafeArrayGetDim(vArray)     ' <- Type mismatch
End Sub

Solution 14 - Arrays

If the array is a string array, you can use the Join() method as a test:

Private Sub Test()

    Dim ArrayToTest() As String

    MsgBox StringArrayCheck(ArrayToTest)     ' returns "false"

    ReDim ArrayToTest(1 To 10)

    MsgBox StringArrayCheck(ArrayToTest)     ' returns "true"

    ReDim ArrayToTest(0 To 0)

    MsgBox StringArrayCheck(ArrayToTest)     ' returns "false"

End Sub


Function StringArrayCheck(o As Variant) As Boolean

    Dim x As String

    x = Join(o)

    StringArrayCheck = (Len(x) <> 0)

End Function

Solution 15 - Arrays

My only problem with API calls is moving from 32-bit to 64-bit OS's.
This works with Objects, Strings, etc...

Public Function ArrayIsInitialized(ByRef arr As Variant) As Boolean
    On Error Resume Next
    ArrayIsInitialized = False
    If UBound(arr) >= 0 Then If Err.Number = 0 Then ArrayIsInitialized = True
End Function

Solution 16 - Arrays

You can solve the issue with Ubound() function, check if the array is empty by retrieving total elements count using JScript's VBArray() object (works with arrays of variant type, single or multidimensional):

Sub Test()
    
    Dim a() As Variant
    Dim b As Variant
    Dim c As Long
    
    ' Uninitialized array of variant
    ' MsgBox UBound(a) ' gives 'Subscript out of range' error
    MsgBox GetElementsCount(a) ' 0
    
    ' Variant containing an empty array
    b = Array()
    MsgBox GetElementsCount(b) ' 0
    
    ' Any other types, eg Long or not Variant type arrays
    MsgBox GetElementsCount(c) ' -1
    
End Sub

Function GetElementsCount(aSample) As Long
    
    Static oHtmlfile As Object ' instantiate once
    
    If oHtmlfile Is Nothing Then
        Set oHtmlfile = CreateObject("htmlfile")
        oHtmlfile.parentWindow.execScript ("function arrlength(arr) {try {return (new VBArray(arr)).toArray().length} catch(e) {return -1}}"), "jscript"
    End If
    GetElementsCount = oHtmlfile.parentWindow.arrlength(aSample)
    
End Function

For me it takes about 0.4 mksec for each element + 100 msec initialization, being compiled with VB 6.0.9782, so the array of 10M elements takes about 4.1 sec. The same functionality could be implemented via ScriptControl ActiveX.

Solution 17 - Arrays

If ChkArray(MyArray)=True then
   ....
End If

Public Function ChkArray(ByRef b) As Boolean
    On Error goto 1
    If UBound(b) > 0 Then ChkArray = True
End Function

Solution 18 - Arrays

There are two slightly different scenarios to test:

  1. The array is initialised (effectively it is not a null pointer)
  2. The array is initialised and has at least one element

Case 2 is required for cases like Split(vbNullString, ",") which returns a String array with LBound=0 and UBound=-1. Here are the simplest example code snippets I can produce for each test:

Public Function IsInitialised(arr() As String) As Boolean
  On Error Resume Next
  IsInitialised = UBound(arr) <> 0.5
End Function

Public Function IsInitialisedAndHasElements(arr() As String) As Boolean
  On Error Resume Next
  IsInitialisedAndHasElements = UBound(arr) >= LBound(arr)
End Function

Solution 19 - Arrays

The title of the question asks how to determine if an array is initialized, but, after reading the question, it looks like the real problem is how to get the UBound of an array that is not initialized.

Here is my solution (to the the actual problem, not to the title):

Function UBound2(Arr) As Integer
  On Error Resume Next
  UBound2 = UBound(Arr)
  If Err.Number = 9 Then UBound2 = -1
  On Error GoTo 0
End Function

This function works in the following four scenarios, the first three that I have found when Arr is created by an external dll COM and the fourth when the Arr is not ReDim-ed (the subject of this question):

  • UBound(Arr) works, so calling UBound2(Arr) adds a little overhead, but doesn't hurt much
  • UBound(Arr) fails in in the function that defines Arr, but succeeds inside UBound2()
  • UBound(Arr) fails both in the function that defines Arr and in UBound2(), so the error handling does the job
  • After Dim Arr() As Whatever, before ReDim Arr(X)

Solution 20 - Arrays

I see a lot of suggestions online about how to tell if an array has been initialized. Below is a function that will take any array, check what the ubound of that array is, redimension the array to ubound +1 (with or without PRESERVER) and then return what the current ubound of the array is, without errors.

Function ifuncRedimUbound(ByRef byrefArr, Optional bPreserve As Boolean)
On Error GoTo err:

1: Dim upp%:           upp% = (UBound(byrefArr) + 1)

errContinue:

If bPreserve Then
         ReDim Preserve byrefArr(upp%)
Else
         ReDim byrefArr(upp%)
End If

ifuncRedimUbound = upp%


Exit Function
err:
If err.Number = 0 Then Resume Next
    If err.Number = 9 Then ' subscript out of range (array has not been initialized yet)
             If Erl = 1 Then
                         upp% = 0
                         GoTo errContinue:
             End If
    Else
               ErrHandler.ReportError "modArray", ifuncRedimUbound, "1", err.Number, err.Description
    End If
End Function

Solution 21 - Arrays

This worked for me, any bug in this?

If IsEmpty(a) Then
    Exit Function
End If

MSDN

Solution 22 - Arrays

Dim someArray() as Integer    

If someArray Is Nothing Then
    Debug.print "this array is not initialised"
End If

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionravenView Question on Stackoverflow
Solution 1 - ArraysGSergView Answer on Stackoverflow
Solution 2 - ArraysravenView Answer on Stackoverflow
Solution 3 - ArraysravenView Answer on Stackoverflow
Solution 4 - ArraysravenView Answer on Stackoverflow
Solution 5 - ArraysRS ConleyView Answer on Stackoverflow
Solution 6 - ArraysiCodeInVB6View Answer on Stackoverflow
Solution 7 - ArraysFrancisco CostaView Answer on Stackoverflow
Solution 8 - ArraysSJ00View Answer on Stackoverflow
Solution 9 - ArraysFrodoView Answer on Stackoverflow
Solution 10 - ArraysjorgeView Answer on Stackoverflow
Solution 11 - ArraysDarrenMBView Answer on Stackoverflow
Solution 12 - ArraysKip DensleyView Answer on Stackoverflow
Solution 13 - ArraysScruffView Answer on Stackoverflow
Solution 14 - ArraysPerry PedersonView Answer on Stackoverflow
Solution 15 - ArraysTim.FView Answer on Stackoverflow
Solution 16 - ArraysomegastripesView Answer on Stackoverflow
Solution 17 - ArraysSenchiu PeterView Answer on Stackoverflow
Solution 18 - ArraysBucket123View Answer on Stackoverflow
Solution 19 - ArraysstenciView Answer on Stackoverflow
Solution 20 - ArraysEvan TOderView Answer on Stackoverflow
Solution 21 - Arraysmadhu_pView Answer on Stackoverflow
Solution 22 - ArraysAndrew Harmel-LawView Answer on Stackoverflow