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 strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Dynamic Array 1

Status
Not open for further replies.

mindready

Programmer
Dec 22, 2002
15
CA
Let say I define an dynamic array

Dim my() as string

Give this to a procedure
Call myProcedure(my)

Sub myProcedure(byref InputStr() as string)
Redim InputStr(1 to 2)
ect...
InputStr(1) ="blabla"
InputStr(2) ="blabla2"
end sub

how can I check the bound of "my" without getting error
in the case that "my" has not been modified by the procedure
and is still empty?

1-function IsNull don't say anything
2-Lbound give an error "subscript out of range"

I know I can use "On Error Go to" or whatever... but don't like it.

There is sureley of function to do this

thanks






 
I don't believe there is a clean way of doing what you are asking.

I know of only 3 solutions.

1. -Don't call any bound checking on the array if you can help it.

2. -Do as you already know - using on error goto

3. -I have seen people redim the array with 0 - and reserve that place simply for bound checking.

so

for i=1 to ubound(my)
code here
next i

- if this is done, then the for loop will never execute, and you won't get any errors. I personally think this way sucks too. No one wants to reserve the 0th position for bound checking.


If anyone has a better way -I'm all ears.

 
Have you already looked at the LBound and UBound functions?

You can easily create a loop like this:

Dim idx as Long

For idx = LBound(my) to UBound(my)
' ...
Next
 
If you do not like the jump created by "On error goto" then why not use in-line error handling. There is not much more code needed for this.

Just three more lines will be needed:
Err.Clear
On error resume next
x = LBound(my)
if Err.Number <> 0 then....

It is not possible to check the bounds of an array without some kind of error handling I'm afraid...
 
mindready's whole point is that lbound abd ubound raise an error of the array has no elements...
 
Thanks to those who broadened my horizons! I did not know about this behavior. :D

Notice that the local variables window shows an uninitialized string? Also, as soon as you ReDim, this changes. This suggests there IS a way to find this out (but I don't know it yet).

There are a couple of different things you could do:

1) Give your array an initial dimension. (-1 to -1)?
2) A variant on allocating 0 to lower array value: use -1 instead. That leaves 0 for your use.

HTH -- if not, please ignore my ramblings!
 
Yes, there is a way to find it out. Involves a bit of a hack, though, if you want to avoid raising an error (which is probably the easiest way):

[tt]
Option Explicit

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

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

Private Sub Command1_Click()
Dim a() As Long
Dim b(1, 1) As Long

Debug.Print ArrayHasElements(VarPtrArray(a))
Debug.Print ArrayHasElements(VarPtrArray(b))
End Sub


Private Function ArrayHasElements(arrTest As Long) As Boolean
Dim lSourceAddress As Long
Dim sSource As SafeArray

CopyMemory lSourceAddress, ByVal arrTest, 4
CopyMemory ByVal sSource, ByVal lSourceAddress, Len(sSource)
ArrayHasElements = sSource.cbElements
End Function

 
Dim my() As String

Sub aaa()
Dim i As Integer
Call myProcedure
For i = LBound(my) To UBound(my) ' 1 to 2
MsgBox my(i)
Next i
' etc ...
End Sub

Sub myProcedure()
ReDim my(1 To 2)
'   ect...
my(1) = &quot;blabla&quot;
my(2) = &quot;blabla2&quot;
End Sub
 
strongm, what you do there looks quite alright (although you're right that it is a lot of work just to get around using a simple error handler).

But have you actually tested this solution (I have not, yet...). I ask this because RtlMoveMemory is a kernel mode method and I am not sure if it will work when you are in plain user mode (as in VB).

Also it seems to me that using a second variable (lSourceAdress) and therefore the first CopyMemory is a bit redundant. Why not use the address you got from VarPtr directly in the CopyMemory?

Also I have my doubts about the second CopyMemory statement. RtlMoveMemory expects you to supply it with a pointer to the memoryblock you want to fill up, so it seems to me that you must pass the SafeArray by reference, not by value...

But then again, I have not tested either your method, nor my method...
 
This is where a variant comes in handy
(every thing in green and black is only for testing):

Sub testarray()
Dim my As Variant
Dim i As Integer

For i = 0 To 1 'used only for testing
Call myProcedure(my, i)

'used only for testing
If IsArray(my) Then
MsgBox &quot;It's an array and has data&quot;
Else
MsgBox &quot;It's Not an array and has NO data&quot;
End If
'reset to variant for this test only
Set my = Nothing
Next i
End Sub

Sub myProcedure(ByRef InputStr As Variant, Optional ByVal bTestNoArray As Boolean = False)
If bTestNoArray Then
'No values set
Else
ReDim InputStr(1 To 2)
InputStr(1) = &quot;blabla&quot;
InputStr(2) = &quot;blabla2&quot;
End If

End Sub [/b][/i][/u][sub]*******************************************************
General remarks:
If this post contains any suggestions for the use or distribution of code, components or files of any sort, it is still your responsibility to assure that you have the proper license and distribution rights to do so!
 
' only works for String arrays however

Private Sub Command1_Click()
Dim x() As String

x = Split(&quot;&quot;)

Debug.Print IsDimensioned(x)

ReDim x(1 To 4)

Debug.Print IsDimensioned(x)
End Sub

Private Function IsDimensioned(x() As String) As Boolean
Debug.Print LBound(x), UBound(x)
IsDimensioned = (LBound(x) <= UBound(x))
End Function
 
>But have you actually tested this solution

No, I have a habit of dumping untried answers in this forum

>RtlMoveMemory is a kernel mode method

No it isn't, so not an issue.

>Why not use the address you got from VarPtr directly in the CopyMemory?

Because I am dereferencing a pointer to a pointer, rather than just a pointer.

>RtlMoveMemory expects you to supply it with a pointer to the memoryblock

I'd carefully read everything there is to read about RtlMoveMemory if I were you.



 

...and only for single dimensioned.

A different, and also useful thought...

But, with that JustinEzequiel, you could also use something like this (but using the Split function counter part: Join and the Len function) as well (also for one dimensioned string arrays):

Sub testarray2()
Dim my() As String
Dim x As String

Call myProcedure2(my)

x = Join(my)
If Len(x) Then

MsgBox &quot;It's an array and has data&quot;
Else
MsgBox &quot;It's Not an array and has NO data&quot;
End If

End Sub

Sub myProcedure2(ByRef InputStr() As String)
Dim bTestNoArray As Boolean
'bTestNoArray = True

If bTestNoArray Then
'No values set
Else
ReDim InputStr(1 To 2)
InputStr(1) = &quot;blabla&quot;
InputStr(2) = &quot;blabla2&quot;
End If

End Sub [/b][/i][/u][sub]*******************************************************
General remarks:
If this post contains any suggestions for the use or distribution of code, components or files of any sort, it is still your responsibility to assure that you have the proper license and distribution rights to do so!
 
LOL - Like I said, no clean way to do it!

If what CCLINT has there works, I'd say that's the best solution other than the inline error checking.

I don't know anything about that kernel memory crap. Looking for something easy here fellas.

Good work though.
 
I like to use an additional boolean variable in my method call. Either that or return a boolean from the function. In either case, the boolean indicates whether the array has data in it.

Private Sub Test()
dim ar() as variant
dim booHasBounds as boolean

booHasBounds = MyProc(ar)
If booHasBounds then
'....Use the Array
End If
End Sub

Private Function MyProc(InputStr() as string) as Boolean
Redim InputStr(1 to 2)

InputStr(1) =&quot;blabla&quot;
InputStr(2) =&quot;blabla2&quot;
MyProc=true
end sub

This is the cleanest way I've found. If the function is already returning another value, pass the Boolean back as an output parameter. Dave Robinder, MCSD
Consultant
Booz Allen Hamilton
Colorado Springs, CO
(719) 590-6041
 

The drop the &quot;booHasBounds&quot; and just code:

Sub testarray2()
If MyProc(ar) Then
'Has been dimensioned
End If
End Sub

Private Function MyProc(InputStr() as string) as Boolean
'SomeCode whether to initialize the array
If SomeCode Then
MyProc = True
Redim InputStr(1 to 2)
InputStr(1) =&quot;blabla&quot;
InputStr(2) =&quot;blabla2&quot;
MyProc=true
End If
end sub

[/b][/i][/u][sub]*******************************************************
General remarks:
If this post contains any suggestions for the use or distribution of code, components or files of any sort, it is still your responsibility to assure that you have the proper license and distribution rights to do so!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top