Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I test if a dynamic array has elements? 2

Status
Not open for further replies.

tedsmith

Programmer
Nov 23, 2000
1,762
0
0
AU
First I dimension an array
Public MyArray() as string

Then I feed it with received data buffer of items separated by VbCr thus -
MyArray=Split(Buffer,VbCr)

I can test if data was received OK later by Ubound(MyArray) giving the number of elements or testing the first element.

But my problem is if the data is not received, the Array still has no elements. Ubound(MyArray) and any attempt to test a MyArray element gives an error.
IsEmpty(MyArray) gives false whether the data is there or not.

Apart for using a separate On Error Goto or having an additional flag, is there any other way of directly testing if a dynamic array is empty or not?
 

I found another method.

Just use the NOT operator on the array!
A return of -1 means it is not dimensioned.
 
Try it out.
For a string array it will return
vbArray + vbString = 8200

It will return this whether it has been dimensioned or not.
That is not helpful for determining whether the array has elements or not, as VarType just lets us only know if it is an array variable and of what type.
Code:
Dim MyArray() as String
Debug.? VarType(MyArray)

Redim MyArray(10)
Debug.? VarType(MyArray)

What the NOT operator used on the array does is return -1 if it is not dimensioned:
Code:
Dim MyArray() as String
Debug.? (Not MyArray) <> -1

Redim MyArray(10)
Debug.? (Not MyArray) <> -1
If you check for this first, then you know if it has elements or not. Helpful also when using a For/Each on the Array.

 
Thanks, using your idea, I found that this was the most consistent way of testing whether a dynamic array was in use. You seem to have to use an intermediate variable - but not when you debug.print it!

Dim ArrayState as long
Dim MyArray()

Sub Test()
ArrayState = Not MyArray
If ArrayState= -1 then
'Array has not been used
Else
'Array has some data in it. Can be negative or positive long integer.
End If
End Sub

If you say If Not MyArray =false or If MyArray = True, it doesn't work
 
>You seem to have to use an intermediate variable
I don't understand.

If Not myArray Then
Msgbox "Not Initialised"
End If

works fine for me

>If you say If Not MyArray =false or If MyArray = True, it doesn't work

Possibly because -1 is TRUE, not FALSE

 
Yes but if you just say If Not myArray you always get a positive or negative number and never a zero. The uninitialized array was different to one that had been initialized but with all elements were still equal to "".

Instead of
'Array has some data in it. Can be negative or positive long integer.
I should have said
'Array has been initialized and may have some data in it or not.

My example was the only one I could get to work in all circumstances to tell if it was initialised.
 
If (Not myArray) = -1 Then
Msgbox "Not Initialised"
End If

or

If (Not myArray) = True Then
Msgbox "Not Initialised"
End If


(and yes, there is clearly some interesting casting going on behind the scenes here)

 
False/Ture?
There seems to be a misunderstanding.
I never wrote that this is what we are checking for.

And
>If MyArray = True Then
does not work (error).

And
If Not MyArray =false

is not what we are looking for, and will never be False.

What we are looking for, is what INTEGER value

(Not MyArray)

returns!

It will return -1 if the array is not initialized, or -###### if it is initialized.
So CBool(Not MyArray) always returns TRUE.

What we are looking for then, is if
(NOT MyArray) = -1
or
(NOT MyArray) = -####### (some neg. number).


So, you should have no problem with Debug.? if checking for then retun value to be -1 or not.

See my faq222-7138 for sample code.
 

Didn't see the last two posts.
I should have refreshed prior to posting....

 


>(and yes, there is clearly some interesting casting going on behind the scenes here)

Quite interesting indeed.
 
I've figured out what it is doing ...


And I'm quite pleased, because it is basically doing exactly the same thing as my ArrayHasElements code in thread222-836312. My code is just more explicit about it.

If we change the ArrayHasElements function's return type to a Long instead of a Boolean (which also means that we can get dispense with the intermediate lResult) we can see this in action:
Code:
[blue]Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)
Private Declare Function VarPtrArray Lib "msvbvm60.DLL" Alias "VarPtr" (Ptr() As Any) As Long

Public Function ArrayHasElements(arrTest As Long) As Long 'Boolean
    CopyMemory ArrayHasElements, ByVal arrTest, 4 ' This is evil code
End Function

Public Sub Example()
    Dim myarray() As Long
    
    Debug.Print Not myarray
    Debug.Print Not ArrayHasElements(VarPtrArray(myarray))
    
    ReDim myarray(5) As Long
    
    Debug.Print Not myarray
    Debug.Print Not ArrayHasElements(VarPtrArray(myarray))
End Sub[/blue]

In other words, the Not is forcing VB to treat the variable as a simple pointer, and to read (and invert, because that is what Not does) the 4 bytes at that location.
 
I have been reading this thread with interest but because I am such an rank beginner I hesitated to respond. I can not restrain myself any longer. The tread started with:

snip....
First I dimension an array
Public MyArray() as string

Then I feed it with received data buffer of items separated by VbCr thus -
MyArray=Split(Buffer,VbCr)

I can test if data was received OK later by Ubound(MyArray) giving the number of elements or testing the first element.

But my problem is if the data is not received, the Array still has no elements. Ubound(MyArray) and any attempt to test a MyArray element gives an error.
IsEmpty(MyArray) gives false whether the data is there or not.

..... end snip

Since the problem is caused by not having an element in the array (data not received) wouldn't your problem be solved by placing a data element in the first position of the array right after defining it? Then you could check if the first element was overwritten (Thus data was received). Just make sure that the data you place (something like a secure PW string) in the first element could not have come from your buffer.
 
>wouldn't your problem be solved by placing a data element in the first position of the array right after defining it?

I suggested something like that way back in the thread:

strongm said:
Why not at the point where you originally dim (and presumably at some point clear) the dynamic string array immediately call something like

MyArray=Split("",VbCr)

MyArray now has a Ubound of -1. So you can now safely test Ubound later in your program safe in the knowledge that you won't get an error and a result of -1 will indicate that no data has been received.

But Ted always likes to do things his own idiosyncratic way, and doesn't really like alternatives; we've got used to that.
 
Thanks to all contributors to this thread.

I don't quite understand >In other words, the Not is forcing VB to treat the variable as a simple pointer, and to read (and invert, because that is what Not does) the 4 bytes at that location.

It was only when I realised (Not myArray) produced a Long Integer and not Boolean that I was able to offer my last simple solution - even if it is slightly different.
I originally made the mistake of assuming Not in this case was only associated with Boolean. Any number other than zero came out as true.

To summarise: There originally appeared to me that there were four different circunstances under which a dynamic array can exist.

1. Where it has been originally declared with no elements and no data has been fed to it Dim myArray()
(Ubound(myArray) gives an error. (Not myArray) = -1)

2. Where it has been redimensioned later in code by REDIM myArray(20) and no data has yet been fed to it.
(Ubound(myArray)=0. (Not myArray) = -235413)

3. Where it has been redimensioned later in code by REDIM myArray(20) and some data has yet been fed to it. (Ubound(myArray)= a number. (Not myArray) = 123456)

4. Where the array has been automatically dimensioned to the number of elements from a split statement
MyArray = Split(InData,vbcr)
(Ubound(myArray)= a number. (Not myArray) = 123456)

If anyone disagrees, please feel free to tell me.

I was originally trying consistently to detect condition 1 above.

Don't worry SidYuca, some of us will always be beginners in programming, it keeps us feeling young.
I am never just trying to be different but always looking to do things in a more simple way.
 
That's OK Ted. When you are a learner of age (72) you will have learned that one does not have the luxury to spend as much time as one would like to make it simple. Sometimes just getting it to 'work' is satisfaction enough!
 
>When you are a learner of age
Oh, wait til you find how old Ted is ...
 
tedsmith said:
I don't quite understand >In other words, the Not is forcing VB to treat the variable as a simple pointer, and to read (and invert, because that is what Not does) the 4 bytes at that location.

First of all you need to understand that all variables in VB are actually pointers. If I do an assignment such as

myVariable = 12

what actually happens is that myVariable actually refers to a long (4 byte) pointer to a memory location containing the value 12; the number of bytes used to actually store the variable at that memory location depends on the variable type.

I've put this together quite quickly and on re-reading I'm not sure it clarifies things as much as I'd have liked. Sorry.
 
Ooh - that's wierd, that's the short version. Hang on ...

tedsmith said:
I don't quite understand >In other words, the Not is forcing VB to treat the variable as a simple pointer, and to read (and invert, because that is what Not does) the 4 bytes at that location.

[Note: the following is a slight simplification]

First of all you need to understand that all variables in VB are actually pointers. If I do an assignment such as

DIm myVariable as Long
myVariable = 12

what actually happens is that myVariable actually refers to a long (4 byte) pointer to a memory location containing the value 12 (the number of bytes used to actually store the variable at that memory location depends on the variable type), and VB tracks the type of variable being pointed to by each variable

VB hides the fact that this is a pointer from VB programmers in a number of ways, but we can reveal the value of (simple) pointers by using the undocumented VarPtr function (along with ObjPtr and StrPtr)

So Debug.Print VarPtr(myVariable) would display the address of the memory location containing the lowest byte of the 4 bytes representing our Long variable.

Note that to do this with an array we have to use VarPtrArray, as shown in my ArrayHasElements example above, because VarPtr doesn't understand arrays

Ok, so the a pointer to an array in VB actually points to a somewhat more sophisticated API structure called a SAFEARRAY, which has a bunch of information about the array preceeding a pointer to the actual data itself (a UDT version of this layout can be seen in a slightly more expository version of my example in thread222-1181777).

Now, in my original version of ArrayHasElements I simply checked whether the first 4 bytes of the structure contain 0, and returned a boolean. But in the version in the example above, it actually returns the value of those first 4 bytes, treating it as a Long.

Right, so that's my code. But what about VB. Well, when we do something like:

Debug.? myArray

VB knows that myArray is an array, and tries to treat it as such.

But using Not is clearly forcing VB to treat the variable as a simple pointer to a value contained in a DWORD (i.e. 4 bytes of memory). And when applied in unary fashion to a Not inverts all the bits of that value. VB then treats the resulting value as a signed long.
 
Thanks
I am really old, old enough to be aware of byte manipulation and how variables are stored from my old days of machine language programming on a 6502 & 6800 when you used fixed memory locations to store data - a forerunner to variables.

Regarding a dimensioned only dynamic Array, when it has no dimensions, it's bytes containing the address in memory where it's future data are to be stored, would have a value of zero. In other words NO memory address for data has yet been reserved. The NOT just inverts it to give -1.
eg. (Not MyArray) = -1

Similarly when you feed an array (without the brackets) directly into a propertybag you are presumably telling it the address of the memory where the data is and how much data to get. It is reasonable to assume therefore that the (Not Array) then is the NOT of the (zero) data address that doesn't yet exist.

If the above is true, the question should then be why does VB produce an error if you try to use the same Array but without the NOT? Reason tells me the test without the NOT before dimensioning should have been zero. eg. (MyArray = 0)

I wouldn't be surprised if this error was simply some sort of oversight or glitch in the original design of the VB6 error checking routine for this situation!

I don't think that (Not MyArray) is like your HasArrayElements returning the pointer to the memory containing the number of dimensions because it varies wildly as you vary only the data in the array so it is pointing to the data memory.
 
You are, I'm afraid, completely incorrect Ted. And once again you appear not to have properly looked at example code which illustrates exactly what is going on, code which does not return the number of dimensions (the NumberOfDims code referenced in my previous post is doing something slightly different, and was simply to provide the structure of a SAFEARRAY; sorry if that muddied the waters a little)

Just to make it clear, when you Dim a dynamic array you get a pointer to the structure (defined in VB terms) I show below, all the members of which are initialised to zero. In other words, the pointer for an uninitialised dynamic array is not 0 (and if you'd used VarPtrArray you could have seen that this is the case)

Private Type SafeArray
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
End Type

cDims and fFeatures make up the first 4 bytes, and it is those 4 bytes that are returned by my ArrayHasElements code example in this thread and (in NOTted form) from the NOT myArray solution. And it is the contents of fFeatures that gives the result its 'widely varying' value, as it describes what type of data is stored in the SAFEARRAY and how the array is allocated using the following bits:
Code:
FADF_AUTO 0x0001 
 An array that is allocated on the stack. 
 
FADF_STATIC 0x0002 
 An array that is statically allocated. 
 
FADF_EMBEDDED 0x0004 
 An array that is embedded in a structure. 
 
FADF_FIXEDSIZE 0x0010 
 An array that may not be resized or reallocated. 
 
FADF_RECORD 0x0020 
 An array that contains records. When set, there will be a pointer to the IRecordinfo interface at negative offset 4 in the array descriptor. 
 
FADF_HAVEIID 0x0040 
 An array that has an IID identifying interface. When set, there will be a GUID at negative offset 16 in the safe array descriptor. Flag is set only when FADF_DISPATCH or FADF_UNKNOWN is also set. 
 
FADF_HAVEVARTYPE 0x0080 
 An array that has a VT type. When set, there will be a VT tag at negative offset 4 in the array descriptor that specifies the element type. 
 
FADF_BSTR 0x0100 
 An array of BSTRs. 
 
FADF_UNKNOWN 0x0200 
 An array of IUnknown*. 
 
FADF_DISPATCH 0x0400 
 An array of IDispatch*. 
 
FADF_VARIANT 0x0800 
 An array of VARIANTs. 
 
FADF_RESERVED 0xF0E8 
 Bits reserved for future use.

I don't make this stuff up, you know.


As for
tedsmith said:
why does VB produce an error if you try to use the same Array but without the NOT
that's exactly what I'd expect to happen. That's the documented behaviour. The unusual behaviour is what happens when NOT is used and, as we said 3 days ago
strongm said:
there is clearly some interesting casting going on behind the scenes here
with VB clearly treating what was an array as a Long


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top