-
Dec 8th, 2005, 12:48 AM
#1
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.
-
May 30th, 2006, 10:57 PM
#2
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.
-
Jun 7th, 2006, 06:28 AM
#3
Frenzied Member
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:
Public Type SafeArray1d
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
cElements As Long
lLbound As Long
End Type
Public Const ARRAY_NOT_INITIALISED As Long = -1
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, source As Any, ByVal Length As Long)
Public Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
Public Function MaxOrdinal(ppSA As Long) As Long
' Eg: n = MaxOridinal(VarPtrArray(MyArray))
Dim SA As SafeArray1d
Dim pSA As Long
'***********************************************
'* Deref ppSA. C equivalent: = pSa=*ppSA . . .
'***********************************************
CopyMemory pSA, ByVal ppSA, 4
'*****************************************************
'* If we have a pointer then check to see how
'* many elements we are allowed. If the array
'* is not dimensioned then pSA will not be valid . . .
'*****************************************************
If pSA Then
CopyMemory SA, ByVal pSA, LenB(SA)
MaxOrdinal = (SA.cElements - 1)
Else
MaxOrdinal = ARRAY_NOT_INITIALISED
End If
End Function
Last edited by si_the_geek; Jun 7th, 2006 at 07:37 PM.
-
Jul 8th, 2008, 08:40 PM
#4
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.
-
Jul 8th, 2008, 10:34 PM
#5
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.
-
Jun 18th, 2010, 07:13 AM
#6
Addicted Member
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
-
Jun 18th, 2010, 04:53 PM
#7
Re: Classic VB - How do I check if array has been initialized?
Another way:
vb Code:
Option Explicit
Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Function AryIsDimmed(ByVal lArrayPointer As Long) As Boolean
Dim X As Long
CopyMemory X, ByVal lArrayPointer, 4
If X = 0 Then
AryIsDimmed = False
Else
AryIsDimmed = True
End If
End Function
'example usage
Private Sub Form_Load()
Dim X() As Long
Debug.Print AryIsDimmed(VarPtrArray(X))
ReDim X(0)
Debug.Print AryIsDimmed(VarPtrArray(X))
Erase X
Debug.Print AryIsDimmed(VarPtrArray(X))
End Sub
Software I use and highly recommend: Opera, Miranda IM, Peerblock, Winamp, Unlocker Assistant, JoyToKey, Virtual CloneDrive, Secunia PSI, ExplorerXP, GOM Player, Real Alternative, Quicktime Alternative,Sumatra PDF, and non-freeware: Photoshop and VB6().
My codebank: AllRGB, Rounded Rectangle(math), Binary Server, Buddy Paint, LoadPictureGDI+, System GUID/Volume Serial, HexToAsc, List all processes and their paths, quasiString matching
Strings(search, extraction, retrieval etc): Retrieve BBCode Link from HTML, RemoveBetween ()'s, strFindBetween(str1,str2), Insert text in HTML, HTML - GetSpanByID
-
Jun 18th, 2010, 08:48 PM
#8
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.
-
May 16th, 2015, 06:22 PM
#9
Fanatic Member
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
-
Jan 17th, 2017, 02:08 PM
#10
Junior Member
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
Originally Posted by Merri
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|