tags:

views:

2132

answers:

8

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?

+2  A: 

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).

raven
Usage of the `Not` hack is not advised because isn't actually a language feature. Instead, it results from a *bug* in the compiler and the behaviour may have unexpected consequences. Use GSerg's way instead.
Konrad Rudolph
@Konrad, that's interesting. Do you know of a source that describes more about the bug?
jtolle
@jtolle: unfortunately, no. As far as I know it was never acknowledged in the MSDN but it’s been known to the VB6 community for years.
Konrad Rudolph
A: 
Dim someArray() as Integer    

If someArray Is Nothing Then
    Debug.print "this array is not initialised"
End If
Andrew Harmel-Law
That causes a "Type mismatch" error.
raven
Are you thinking of VB.NET? You might want to delete this answer Andrew
MarkJ
A: 

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

Perry Pederson
+6  A: 

I use this:

Public Declare Function GetMem4 Lib "msvbvm60" (ByVal pSrc As Long, ByVal pDst As Long) As Long
Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (arr() As Any) As Long


Public Function ArrayExists(ByVal ppArray As Long) As Long
  GetMem4 ppArray, VarPtr(ArrayExists)
End Function

Usage:

? ArrayExists(ArrPtr(someArray))

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

GSerg
Sorry this answer wasn't chosen as it is the most elegant and flexible solution. I'm going to tuck this away for future use. Thanks
Praesagus
+4  A: 

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.

RS Conley
You know, the example originally had that syntax but I edited because I thought it was pointless. Now I know better.
raven
This is what I use. Only it is really confusing to read, so I made a function.
Matthew Cole
Karl Peterson says this can cause problems. That's a good enough reason to avoid it IMHO http://groups.google.co.uk/group/microsoft.public.vb.general.discussion/browse_thread/thread/dc204fc379cc5f6b?hl=en#
MarkJ
E.g. try running this code from the IDE. I get an error 16 expression too complex on the last MsgBox. 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
MarkJ
+6  A: 

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.

raven
Beat me to it ;-) That's usually how I do it. One small critique: the above code ignores *any* error that might occur, which is not ideal. For example, if someone passes in a plain Integer by mistake, you would want the function to raise a "Type Mismatch" error back to the caller, not ignore it.
Mike Spross
To expand on my last comment, error 9 ("Subscript out of range") is the specific error that will occur when you pass an uninitialized array to the LBound and UBound functions, so you can assume the array is empty if Err.Number=9. If a different error occurred, then rethrow.
Mike Spross
>> Any problems with it?In compiled .exe, my check takes 0.06 seconds to execute a million times, whlie this one takes 8 seconds (yes, much less in the IDE, but that's not relevant). Exceptions are expensive, you know...
GSerg
+1  A: 

I've been working in VB6 for a few years now and I never knew about either the

If (Not someArray) = -1

or the GetMem4 method of testing for this. I always used a seperate counter to keep track of an array, which is definitely not ideal (I know), but I believed it less to be expensive, performance wise, than the error handling method.

It is obviously an inferior solution to the GetMem4 method for many reasons, but it does have two small advantages, in my opinion, that it is incredibly simple to implement and negates the need for the IsEmpty test when looping through the array.

Just my two cents.

jakdep
+2  A: 

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 Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (arr() As Any) As Long
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 in memory...

  ArrayIsInitialized = (memVal <> 0)                 '...if it does, array is intialized

End Function
raven