Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

VB5 code generation error - lbound/on error resume next

3 views
Skip to first unread message

Craig Powers

unread,
Jun 26, 2002, 10:05:43 AM6/26/02
to
To resurrect something also reported in 1999 (only then, it didn't
receive a helpful answer):

This was the report in 1999 (originally posted by Clive Allwork):
> VB5.0 executes differently depending on if it is compiled to p-code or
> compiled to native code with full optimization. Check the following:
>
> Given an array of integers which is NOT dimensioned:
>
> Dim iArray() as Integer
> Dim rc As Integer
>
> On Error Resume Next
>
> For i = LBound(iArray) To UBound(iArray)
> rc = MsgBox("In array loop i = " & i & ". Terminate loop?", _
> vbYesNo, "Loop Test")
> If rc = vbYes Then
> Exit For
> End If
> Next i
>
> If this code is compiled to p-code the loop executes once.
> If this code is compiled to Native code with full optimization the loop
> becomes an infinite loop.
>
> Any idea why? Any fixes???

I've encountered the same problem, VB5 SP3, optimized for size.

The reply to this message mistakenly thought that the for loop wound
up being infinite because i was never incremented. This is incorrect --
the call to lbound kicks off an infinite loop, as I was able to verify
by trying to set a variable to the bounds of the array. Single-stepping
in WinDbg hung at the assignment, not the loop.

The reply to this message also inquired as to why you would possibly
want to do something like this. In my case, it's because there's no
other way to detect whether the array has been initialized, and I
need to vary the action taken depending on the initialization state.

I was able to work around the problem by using On Error Goto ... with
a test of what would be the first element of the array (if it had any
elements), with the error block resuming to right after the loop.

Google link for the original thread (mind wrapping)
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF8&threadm=01be5c33%242dd87300%24a806155a%40emcp04067a.emc.mapllc.com&rnum=1&prev=/groups%3Fas_q%3D%2522on%2520error%2520resume%2520next%2522%2520lbound%26ie%3DUTF-8%26oe%3DUTF8%26as_ugroup%3Dmicrosoft.public.vb.bugs%26lr%3D%26hl%3Den

Or a shortened version (not sure how long until this expires):
http://minilien.com/?8lgGHbBk6v

Peter Young

unread,
Jun 26, 2002, 5:27:34 PM6/26/02
to
Hi Craig,

While this doesn't exactly address the problem you've mentioned, it will get you around the issue. All you need to do is wrap the
LBound and UBound calls, or create an ArrayIsEmpty call and test the array first.

The simplest approach uses error handling.
e.g. (warning: I'm just typing this now, so it may not be exactly right)

Public Function ArrayIsEmpty(ByRef vArray As Variant) As Boolean

Dim lUB As Long

'Make sure we have an array:
If IsArray(vArray) Then
On Error Resume Next
lUB = UBound(vArray)
ArrayIsEmpty = Err.Number
Err.Clear
End If

End Function

I personally prefer to not use the error object for a variety of reasons - not the least of which is that you can wreck an
in-process error handler when you call this. Instead, you can use the SafeArray APIs to look at the array and figure out what's
going on. (VB arrays are SafeArrays.)

Here are a couple of routines I use to deal with the fact the LBound and UBound fail when the array is empty:

Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(Destination As Any, _
Source As Any, _
ByVal Length As Long)
Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByVal pSA As Long) As Long
Private Declare Function SafeArrayGetUBound Lib "oleaut32.dll" _
(ByVal pSA As Long, _
ByVal nDim As Long, _
ByRef plUbound As Long) As Long

Public Function ArrayIsEmpty(ByRef vArray As Variant) As Boolean

Dim pSA As Long
Const VARIANT_DATA_OFFSET As Long = 8

'Make sure we have an array:
If IsArray(vArray) Then

'Try to get the pointer:
CopyMemory pSA, ByVal VarPtr(vArray) + VARIANT_DATA_OFFSET, 4

If pSA Then
'Might have been VT_BYREF, and we got the pointer to the pointer.
'Dereference the pointer to get the actual pointer. If zero, then
'the array is empty:
CopyMemory pSA, ByVal pSA, 4
ArrayIsEmpty = (pSA = 0)
Else
'Pointer is zero so the array is empty:
ArrayIsEmpty = True
End If

End If

End Function

'This routine returns -1 for an empty array. It will not fail if the array is empty.
Public Function UBoundEx(ByRef vArray As Variant, _
Optional ByVal lDimension As Long = 1) As Long

Dim iDataType As Integer
Dim pSA As Long
Dim lRet As Long
Const VT_BYREF = &H4000
Const VARIANT_DATA_OFFSET As Long = 8

If IsArray(vArray) Then

'Try to get the pointer:
CopyMemory pSA, ByVal VarPtr(vArray) + VARIANT_DATA_OFFSET, 4

If pSA Then

'If byref then deref the pointer to get the actual pointer:
CopyMemory iDataType, vArray, 2
If iDataType And VT_BYREF Then
CopyMemory pSA, ByVal pSA, 4
End If

If pSA Then
If lDimension > 0 Then
If lDimension <= SafeArrayGetDim(pSA) Then
SafeArrayGetUBound pSA, lDimension, UBoundEx
Else
UBoundEx = -1
End If
Else
Err.Raise vbObjectError Or 10000, "UBoundEx", "Invalid Dimension"
End If
Else
UBoundEx = -1
End If
Else
UBoundEx = -1
End If
Else
Err.Raise vbObjectError Or 10000, "UBoundEx", "Not an array"
End If

End Function

I never made an LBoundEx, though the concept is the same.

Hope that helps,
Pete

"Craig Powers" <eni...@hal-pc.org> wrote in message news:3D19CA37...@hal-pc.org...

Craig Powers

unread,
Jun 27, 2002, 10:18:46 AM6/27/02
to
Peter Young wrote:
>
> While this doesn't exactly address the problem you've mentioned, it will get you around the issue. All you need to do is wrap the
> LBound and UBound calls, or create an ArrayIsEmpty call and test the array first.

Well, as I noted, I was able to work around the problem using On Error
GoTo instead of On Error Resume Next. I posted my message to get more
information out there, since I uncovered only the one thread I linked
(which had a relatively useless response). In that regard, your
message certainly is a big help, and much appreciated.


Speaking of On Error ..., I think the code immediately below may still
manifest the bug, though it's possible that shifting the array into
a variant fixes it. The specific problem is that attempting to read
the bounds in the presence of On Error Resume Next kicks off an
infinite loop.

> The simplest approach uses error handling.
> e.g. (warning: I'm just typing this now, so it may not be exactly right)
>
> Public Function ArrayIsEmpty(ByRef vArray As Variant) As Boolean
>
> Dim lUB As Long
>
> 'Make sure we have an array:
> If IsArray(vArray) Then
> On Error Resume Next
> lUB = UBound(vArray)
> ArrayIsEmpty = Err.Number
> Err.Clear
> End If
>
> End Function
>
> I personally prefer to not use the error object for a variety of reasons - not the least of which is that you can wreck an
> in-process error handler when you call this. Instead, you can use the SafeArray APIs to look at the array and figure out what's
> going on. (VB arrays are SafeArrays.)
>
> Here are a couple of routines I use to deal with the fact the LBound and UBound fail when the array is empty:

Pedant:
This should be named MoveMemory, since there is a separate RtlCopyMemory
function (the distinction is that MoveMemory will function properly in
the presence of overlapping blocks of memory, while CopyMemory makes no
guarantees).

> Private Declare Sub CopyMemory Lib "kernel32" _
> Alias "RtlMoveMemory" _
> (Destination As Any, _
> Source As Any, _
> ByVal Length As Long)

[snip pretty nifty extension functions]

In my case, I was after a quick workaround (and found one), but working
directly with the SafeArray APIs is a good idea and I may do so in the
future. I definitely like your extension functions.

Peter Young

unread,
Jun 27, 2002, 11:51:56 AM6/27/02
to
> a variant fixes it. The specific problem is that attempting to read
> the bounds in the presence of On Error Resume Next kicks off an
> infinite loop.
I've had other trouble with On Error Resume Next...often a conditional that fails will result in execution of the 'True' block of
code whether the expression should've been True or False, so I really use that with caution. It's dangerous.

> Pedant:
> This should be named MoveMemory, since there is a separate RtlCopyMemory
> function (the distinction is that MoveMemory will function properly in
> the presence of overlapping blocks of memory, while CopyMemory makes no
> guarantees).
>
> > Private Declare Sub CopyMemory Lib "kernel32" _
> > Alias "RtlMoveMemory" _
> > (Destination As Any, _
> > Source As Any, _
> > ByVal Length As Long)
>

Yeah, I know, but the 'standard' has set it at CopyMemory. For a discourse on the topic, go here and scroll down to "CopyMemory: A
Strange and Terrible Saga":
http://www.mvps.org/vb/hardcore/html/bringyourhatchet.htm

Best Regards,
Pete


Craig Powers

unread,
Jun 27, 2002, 6:47:50 PM6/27/02
to
Peter Young wrote:
>
> Yeah, I know, but the 'standard' has set it at CopyMemory. For a discourse on the topic, go here and scroll down to "CopyMemory: A
> Strange and Terrible Saga":
> http://www.mvps.org/vb/hardcore/html/bringyourhatchet.htm

Interesting read. I had no idea that the misleading (to a C/C++
programmer) use of the name CopyMemory had been Officially Blessed.
Nor, for that matter, did I realize that there was no RtlCopyMemory
export, but then, a quick browse through the bowels of some other low-
level stuff shows that it's not unheard-of for stuff that's listed in
the Platform SDK to actually be implemented in the compiler runtime
or generated code.

Excerpt for comment:
> If you check the Win32 documentation, you’ll see that MoveMemory does
> the same thing as CopyMemory except that it handles overlapped memory
> in a different fashion. I can’t imagine a situation in which a Basic
> programmer would be copying overlapped memory. No reason not to use
> MoveMemory instead. The name CopyMemory seemed more intelligible than
> hmemcpy or MoveMemory, so I used this alias for both 16-bit and
> 32-bit versions:

Clear evidence of lack of a C/C++ background. You use MoveMemory when
there might be overlapped memory, and CopyMemory otherwise. CopyMemory
is more efficient, because it doesn't have to care about whether
there's an overlap, whereas MoveMemory is required to protect against
it.

(I say this, because the way the text is written, it implies the
opposite, that CopyMemory protects against overlapping and Movememory
does not.)

Vlad Vissoultchev

unread,
Jul 3, 2002, 11:19:19 AM7/3/02
to
even better: you use CopyMemory (or memcpy) to replicate elements in an
array like this

memcpy(a+2, a+1, N*sizeof(*a))

and you get second element repeated N times :-)) no overlapping checking,
please!

</wqw>

"Craig Powers" <eni...@hal-pc.org> wrote in message

news:3D1B9616...@hal-pc.org...

0 new messages