Results 1 to 10 of 10

Thread: Classic VB - How do I check if array has been initialized?

  1. #1

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Classic VB - How do I check if array has been initialized?

    One of the common problems when dealing with arrays is to know when the array has been initialized and when it isn't. Luckily there is an easy native VB code solution:

    Not Not ArrayName

    What this does is to take the 32-bit pointer value in the array variable, mirror the bits, and then mirror them again. You now effectively know the pointer to the safe array structure. As a side effect you also know whether you can access the array with LBound and UBound.

    An example code showing a way to make a new array item:
    Code:
        Dim lngNewIndex As Long
        ' check whether our array is initialized: if it is, take UBound and increase it by one
        If Not Not udtItems Then lngNewIndex = UBound(udtItems) + 1
        ' now change the size of the array
        ReDim Preserve udtItems(lngNewIndex)

    Problems & Issues

    There is one major problem: for whatever reason VB IDE does not like this method, and will raise an error in some cases, for example when floating point numbers are coerced to other datatypes. The error does not happen when the program is compiled. Luckily the solution to the issue is short and simple: add this code to run at the beginning of your application.
    Code:
    Private Sub Form_Load()
        Dim IDEbug() As Long
        Debug.Assert Not IDEbug Or App.hInstance
    End Sub
    What happens here is that first we make a call to push up the possible error condition. Then we make a call to App.hInstance: we could make a call pretty much to any VB method, but since hInstance returns a Long number we use that. This, for whatever reason, makes all the following Not ArrayName calls work flawlessly. After the line has once executed in the IDE you can even comment the line and it's effects still remain. Only closing VB and opening it again will reset the condition so that you need to make the call again.

    Finally, since Debug.Assert will not be compiled into the final executable, you have next to nothing to lose. You only have that one extra line of code that should be executed before getting the array's pointer. (Thanks to Milk for pointing out the App.hInstance, it really simplified this problem a great deal.)


    Bonus code

    Here we have a code that gets the dimensions of an array:
    Code:
    Option Explicit
    
    Private Declare Sub GetMem2 Lib "msvbvm60" (Destination As Any, Value As Any)
    
    Public Function ArrayDimensions(ByVal Not_Not_Array As Long) As Integer
        ' have pointer of safe array structure?
        If Not_Not_Array Then
            ' get the dimensions
            GetMem2 ByVal Not_Not_Array, ArrayDimensions
        End If
    End Function
    A sample usage:
    Code:
    Private Sub Form_Load()
        Dim lngTest() As Long, lngA As Long
        ' to fix the ide bug
        Debug.Assert Not lngTest Or App.hInstance
        ' put in some dimensions
        ReDim lngTest(1 To 1, 1 To 2, 1 To 3)
        ' loop the dimensions
        For lngA = 1 To ArrayDimensions(Not Not lngTest)
            ' print out the bounds for each dimension
            Debug.Print lngA, LBound(lngTest, lngA), UBound(lngTest, lngA)
        Next lngA
    End Sub
    Last edited by Merri; Jul 20th, 2008 at 12:23 AM.

  2. #2

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Classic VB - How do I check if array has been initialized?

    Here is an ArrayInit function for those who just want a simple Boolean value and don't want to bother about the Debug.Assert themselves.

    Code:
    Option Explicit
    
    ' usage: If ArrayInit(Not ArrayName) Then ...
    Public Function ArrayInit(ByVal Not_Array As Long) As Boolean
        ArrayInit = Not (NotValue = -1&)
        Debug.Assert App.hInstance
    End Function
    Last edited by Merri; Jul 20th, 2008 at 12:20 AM.

  3. #3
    Frenzied Member yrwyddfa's Avatar
    Join Date
    Aug 2001
    Location
    England
    Posts
    1,253

    Re: Classic VB - How do I check if array has been initialized?

    Here's what I've used for a long time (I didn't know about the Merri method)

    VB Code:
    1. Public Type SafeArray1d
    2.     cDims As Integer
    3.     fFeatures As Integer
    4.     cbElements As Long
    5.     cLocks As Long
    6.     pvData As Long
    7.     cElements As Long
    8.     lLbound As Long
    9. End Type
    10.  
    11. Public Const ARRAY_NOT_INITIALISED As Long = -1
    12.  
    13. Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, source As Any, ByVal Length As Long)
    14. Public Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
    15.  
    16.  
    17. Public Function MaxOrdinal(ppSA As Long) As Long
    18.    
    19.     ' Eg: n = MaxOridinal(VarPtrArray(MyArray))
    20.    
    21.     Dim SA As SafeArray1d
    22.     Dim pSA As Long
    23.  
    24.     '***********************************************
    25.     '* Deref ppSA. C equivalent: = pSa=*ppSA . . .
    26.     '***********************************************
    27.     CopyMemory pSA, ByVal ppSA, 4
    28.    
    29.     '*****************************************************
    30.     '* If we have a pointer then check to see how
    31.     '* many elements we are allowed. If the array
    32.     '* is not dimensioned then pSA will not be valid . . .
    33.     '*****************************************************
    34.     If pSA Then
    35.         CopyMemory SA, ByVal pSA, LenB(SA)
    36.         MaxOrdinal = (SA.cElements - 1)
    37.     Else
    38.         MaxOrdinal = ARRAY_NOT_INITIALISED
    39.     End If
    40.    
    41. End Function
    Last edited by si_the_geek; Jun 7th, 2006 at 07:37 PM.

  4. #4
    Cumbrian Milk's Avatar
    Join Date
    Jan 2007
    Location
    0xDEADBEEF
    Posts
    2,448

    Re: Classic VB - How do I check if array has been initialized?

    For anyone interested Not Arrayname reveals (or not) the inverse of a pointer to the SafeArray structure which VB uses to define arrays. Not Not Arrayname returns the actual pointer or 0 if the array is uninitialised.

    If you do any kind of Array hacking getting the pointer like this is nice and quick (accounting for the floating point error)

    Here is a rather unsafe function which I find useful for when messing with arrays

    Edit: Safe version added, made more friendly, IDE bug resolved better
    Code:
    Option Explicit
    
    Public Type ARRAY_INFORMATION
      Dims As Integer          '00 2 Number of dimensions
      Features As Integer      '02 2 Array construction flags
      ElementSize As Long      '04 4 Size of data elements; 1 byte, 2 integer or boolean, 4 long...
      Locks As Long            '08 4 Number of current locks, a locked Safe Array cannot be redimensioned or destroyed
      DataPtr As Long          '12 4 Data address
      LastDimElements As Long  '16 4 Number of elements in the last dimension
      LastDimLowbound As Long  '20 4 Low bound of the last dimension
    End Type
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Declare Function IsBadReadPtr Lib "kernel32" (ByVal Pointer As Long, ByVal Length As Long) As Long
    
    Public Function ArrayInfo(ByVal Not_ArrayName As Long) As ARRAY_INFORMATION
    'This MUST be called: ArrayInfo(Not arrayname)
    'incorrect usage can crash VB, save work before running
       Not_ArrayName = Not Not_ArrayName
       If Not_ArrayName Then CopyMemory ArrayInfo, ByVal Not_ArrayName, 24
       Debug.Assert App.hInstance '<-- this seems to cure the 'Not Array' IDE bug
    End Function
    
    Public Function ArrayInfoS(ByVal Not_ArrayName As Long) As ARRAY_INFORMATION
    'This should be called: ArrayInfo(Not arrayname)
       Not_ArrayName = Not Not_ArrayName
       If IsBadReadPtr(Not_ArrayName, 24) Then Exit Function
       CopyMemory ArrayInfoS, ByVal Not_ArrayName, 24
       Debug.Assert App.hInstance '<-- this seems to cure the 'Not Array' IDE bug
    End Function
    Last edited by Milk; Jul 13th, 2008 at 06:05 PM.

  5. #5

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Classic VB - How do I check if array has been initialized?

    For those interested, Not Not StringArrayName may be the only way to find out the pointer to a string safearray that doesn't require a TLB declared VarPtr function for the job. At least if I recall this correctly, I played with string array hacking sometime last month.

  6. #6
    Addicted Member
    Join Date
    Jun 2010
    Posts
    182

    Re: Classic VB - How do I check if array has been initialized?

    This is an easy way to check if an Array has been initialized, it uses API though:
    Code:
    'Module General section
    Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByRef saArray() As Any) As Long
    
    'In code
    If SafeArrayGetDim(myArray) <> 0 Then
        MsgBox "Array has been Initialized
    End If

  7. #7
    Fanatic Member FireXtol's Avatar
    Join Date
    Apr 2010
    Posts
    874

    Re: Classic VB - How do I check if array has been initialized?

    Another way:

    vb Code:
    1. Option Explicit
    2. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
    3. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    4.  
    5. Private Function AryIsDimmed(ByVal lArrayPointer As Long) As Boolean
    6. Dim X As Long
    7. CopyMemory X, ByVal lArrayPointer, 4
    8. If X = 0 Then
    9.   AryIsDimmed = False
    10. Else
    11.   AryIsDimmed = True
    12. End If
    13. End Function
    14.  
    15. 'example usage
    16. Private Sub Form_Load()
    17. Dim X() As Long
    18. Debug.Print AryIsDimmed(VarPtrArray(X))
    19. ReDim X(0)
    20. Debug.Print AryIsDimmed(VarPtrArray(X))
    21. Erase X
    22. Debug.Print AryIsDimmed(VarPtrArray(X))
    23. End Sub

  8. #8

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Classic VB - How do I check if array has been initialized?

    Code:
    Dim X() As Long
    
    ' ReDim X(1)
    
    If Not Not X Then
        MsgBox "Initialized"
    Else
        MsgBox "Not initialized"
    End If
    
    Debug.Assert App.hInstance

    Also to see why Debug.Assert is required in IDE:
    Code:
    Option Explicit
    
    Private Sub Form_Load()
        Dim X() As Long
        If Not Not X Then MsgBox "Yay"
        Debug.Assert App.hInstance
        MsgBox CLng(0.1@)
        If Not Not X Then MsgBox "Yay"
        MsgBox CLng(0.1@)
    End Sub
    The second MsgBox will error. Still, performance wise this is the fastest method to check for an initialized array.

  9. #9
    Fanatic Member
    Join Date
    Apr 2015
    Location
    Finland
    Posts
    679

    Re: Classic VB - How do I check if array has been initialized?

    Public Function IsArrayInitialized(ByRef arr As Variant) As Boolean
    'Is array initialized checking
    IsArrayInitialized = False
    On Error Resume Next
    If UBound(arr) >= 0 Then If Err.Number = 0 Then IsArrayInitialized = True Else Err.Clear
    End Function

  10. #10
    Junior Member scrapersNbots.com's Avatar
    Join Date
    Dec 2016
    Location
    Torrington, CT
    Posts
    25

    Re: Classic VB - How do I check if array has been initialized?

    I know this won't apply to all situations but how about
    just setting a boolean flag such as bInitialize, which is declared at
    the beginning of the routine.

    Code:
                                dim  bArrInitialized as boolean
                                dim iArrIndex as integer
    Then when needed later in the routine:

    Code:
                                If bArrInitialized Then
                                          iArrIndex = UBound(Arr) + 1
                                          ReDim Preserve Arr(iArrIndex)
                                Else
                                          bArrInitialized = True
                                          iArrIndex = 0
                                          ReDim Arr(iArrIndex)
                                end if

    or you can declare the variables at the top of a module and use the code anywhere in the module





    Quote Originally Posted by Merri View Post
    One of the common problems when dealing with arrays is to know when the array has been initialized and when it isn't. Luckily there is an easy native VB code solution:

    Not Not ArrayName

    What this does is to take the 32-bit pointer value in the array variable, mirror the bits, and then mirror them again. You now effectively know the pointer to the safe array structure. As a side effect you also know whether you can access the array with LBound and UBound.

    An example code showing a way to make a new array item:
    Code:
        Dim lngNewIndex As Long
        ' check whether our array is initialized: if it is, take UBound and increase it by one
        If Not Not udtItems Then lngNewIndex = UBound(udtItems) + 1
        ' now change the size of the array
        ReDim Preserve udtItems(lngNewIndex)

    Problems & Issues

    There is one major problem: for whatever reason VB IDE does not like this method, and will raise an error in some cases, for example when floating point numbers are coerced to other datatypes. The error does not happen when the program is compiled. Luckily the solution to the issue is short and simple: add this code to run at the beginning of your application.
    Code:
    Private Sub Form_Load()
        Dim IDEbug() As Long
        Debug.Assert Not IDEbug Or App.hInstance
    End Sub
    What happens here is that first we make a call to push up the possible error condition. Then we make a call to App.hInstance: we could make a call pretty much to any VB method, but since hInstance returns a Long number we use that. This, for whatever reason, makes all the following Not ArrayName calls work flawlessly. After the line has once executed in the IDE you can even comment the line and it's effects still remain. Only closing VB and opening it again will reset the condition so that you need to make the call again.

    Finally, since Debug.Assert will not be compiled into the final executable, you have next to nothing to lose. You only have that one extra line of code that should be executed before getting the array's pointer. (Thanks to Milk for pointing out the App.hInstance, it really simplified this problem a great deal.)


    Bonus code

    Here we have a code that gets the dimensions of an array:
    Code:
    Option Explicit
    
    Private Declare Sub GetMem2 Lib "msvbvm60" (Destination As Any, Value As Any)
    
    Public Function ArrayDimensions(ByVal Not_Not_Array As Long) As Integer
        ' have pointer of safe array structure?
        If Not_Not_Array Then
            ' get the dimensions
            GetMem2 ByVal Not_Not_Array, ArrayDimensions
        End If
    End Function
    A sample usage:
    Code:
    Private Sub Form_Load()
        Dim lngTest() As Long, lngA As Long
        ' to fix the ide bug
        Debug.Assert Not lngTest Or App.hInstance
        ' put in some dimensions
        ReDim lngTest(1 To 1, 1 To 2, 1 To 3)
        ' loop the dimensions
        For lngA = 1 To ArrayDimensions(Not Not lngTest)
            ' print out the bounds for each dimension
            Debug.Print lngA, LBound(lngTest, lngA), UBound(lngTest, lngA)
        Next lngA
    End Sub

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width