Post by XPIn a VBScript (.vbs) or an HTA file, using VBScript, can someone please post
generic example code that tests to see if an array is empty?
Thanks much in advance for your assistance.
Arrays do cause quite a bit of confusion as this thread indicates. So I'll
de-mystify them by describing some of the under the hood workings. (Sorry
turned out to be more longwinded than I anticitpated).
VBScript is often described as having typeless variables or only having one
type of variable of the Variant type. A variant can hold any type and can
at any time be re-assigned with a type completely different from the one it
is currently holding.
However VBScript variables actually have two types, Variant and Array of
Variants.
Dim a ' A variant
Dim a() ' An array of variants
The structure of a Variant variable is 16 bytes in size containing in part a
VT field which defines the current type it is holding and the value itself
(if its a fixed size value that fits in 8 bytes, Long or Currency for
example) or a pointer to the value (an object, string or an array).
The structure of an array of variants variable is a pointer to a SAFEARRAY
structure. The SAFEARRAY defines the variant type of its elements, how many
bytes each element is, how many dimensions there are and a pointer to the
beginning of the array data itself. In VBScript the type is always
VT_VARIANT (12) and the element size is therefore always 16 bytes.
The SAFEARRAY structure is extensible in that immediately following it there
are a series (an 'array' you might say) of small structures, one per
dimension. These define the LBound value for the dimension and how many
elements are in the dimension (cElems).
This leads us to why we get "Subscript out of range" when using UBound on
undimensioned arrays.
UBound(a) is in fact short hand for UBound(a, 1) which says get me the
upper bound of the first dimension in the array. The 1 is an index (or
subscript) into that little array of dimension structures that follow the
SAFEARRAY. An undimensioned array has a dimension count of 0 and none of
these structures. Therefore fetching the first element from this 'array' of
dimension structures is 'out of range' for an undimensioned array.
Now take a look at this:-
Dim a
a = Split("", ",")
MsgBox UBound(a)
The array is empty and we get -1 from the UBound what is going on here?
Split is returning a dimensioned array. It has only 1 dimension but that
dimension has an element count of 0 since the input string was empty.
Remember the structure that defines the dimension carries only the LBound
and the element count. The UBound function therefore use this formula to
calculate the UBound :-
lBound + cElems - 1
When both lBound and cElems are 0 then UBound is -1.
This behaviour is actually quite handy, take a look at this:-
Sub ListWords(sLine)
Dim a : a = Split(sLine, " ")
Dim i
For i = 0 To UBound(a)
WScript.Echo a(i)
Next
End Sub
ListWords "Hello World"
ListWords ""
Had Split returned null, empty or an undimensioned array I would have to
take steps in the ListWords procedure to handle that correctly. However as
it is it works fine since For i = 0 to -1 will do nothing.
As Tom pointed out some interfaces can hold properties which if they have a
value will be an array but may also be null. This is another scenario that
may need to be considered when testing for an empty array.
I propose this function for general testing of an array:-
Function TestArray(rvnt)
On Error Resume Next
TestArray = 0
If (VarType(rvnt) And 8192) = 8192 Then
TestArray = UBound(rvnt) - LBound(rvnt) + 1
ElseIf Not (IsEmpty(rvnt) or IsNull(rvnt)) Then
TestArray = -1
End If
End Function
It assumes that a null or empty could potentially be an array but not yet
dimensioned. It returns -1 for if passed a string, long etc, 0 for an empty
array or the number of elements in the first dimension.
Other potentially useful functions might be:-
'Return the number of dimensions in the array, 0 for an non-array.
Function CountDims(rvnt)
On Error Resume Next
Dim i : i = 0
Do While Err.Number = 0
i = i + 1
UBound rvnt, i
Loop
CountDims = i - 1
End Function
and :-
'Return the total number of elements across all dimensions
Function CountElements(rvnt)
On Error Resume Next
CountElements = 1
Dim i : i = 0
Do While Err.Number = 0
i = i + 1
CountElements = CountElements * (UBound(rvnt, i) - LBound(rvnt, i) + 1)
Loop
If i = 1 Then CountElements = 0
End Function
Hope you find this helpful,
--
Anthony Jones - MVP ASP/ASP.NET