-
Aug 5th, 2021, 01:26 AM
#1
Thread Starter
Frenzied Member
How do I properly exit a For-Next loop that contains a With block?
I have this code here that uses a With block within a For-Next loop, and conditionally exits the loop, but the problem is that this leaves the With block still active (the End With line never gets executed), so my later attempt to ReDim the array causes an error. Is there a correct way to conditionally exit the For-Next loop, so I don't need to wait for the loop to complete, but doesn't leave the array locked?
Code:
Private Type MyType
Value As Long
End Type
Private Sub Form_Load()
Dim a() As MyType
Dim n As Long
ReDim a(2)
a(0).Value = 1
a(1).Value = 2
a(2).Value = 3
For n = 0 To 1
With a(n)
If .Value = 2 Then Exit For
End With
Next n
ReDim a(0)
a(0).Value = 123
End Sub
-
Aug 5th, 2021, 01:36 AM
#2
Re: How do I properly exit a For-Next loop that contains a With block?
What's the error message? Can you confirm that changing this:
vb Code:
With a(n)
If .Value = 2 Then Exit For
End With
to this:
vb Code:
If a(n).Value = 2 Then Exit For
alleviates the error? I don't use VB6 so there may be something in the debugger to blame but it would make absolutely no difference in VB.NET.
-
Aug 5th, 2021, 01:37 AM
#3
Addicted Member
Re: How do I properly exit a For-Next loop that contains a With block?
try this:
Code:
Private Type MyType
Value As Long
End Type
Private Sub Form_Load()
Dim a() As MyType
Dim n As Long
Dim vExit As Boolean
ReDim a(2)
a(0).Value = 1
a(1).Value = 2
a(2).Value = 3
For n = 0 To 1
With a(n)
If .Value = 2 Then
vExit = True
Else
'DO THE REST OF THE LOOP
End If
End With
If vExit Then
Exit For
End If
Next n
ReDim a(0)
a(0).Value = 123
End Sub
-
Aug 5th, 2021, 03:33 AM
#4
Re: How do I properly exit a For-Next loop that contains a With block?
or this
Code:
Private Type MyType
Value As Long
End Type
Private Sub Form_Load()
Dim a() As MyType
Dim n As Long
ReDim a(2)
a(0).Value = 1
a(1).Value = 2
a(2).Value = 3
For n = 0 To 1
With a(n)
If .Value = 2 Then n = 1
End With
Next n
ReDim a(0)
a(0).Value = 123
End Sub
cheers,
</wqw>
-
Aug 5th, 2021, 08:56 AM
#5
Re: How do I properly exit a For-Next loop that contains a With block?
My understanding is that the With clause is considered in the text code parsing, before any execution, and the text block between With and End With is parsed specially, and not executed line by line as it was interpreted.
But I could be wrong... who knows.
If that is the case, if you exit before the End With it is irrelevant.
-
Aug 5th, 2021, 09:49 AM
#6
Re: How do I properly exit a For-Next loop that contains a With block?
The problem here is the array.
The With block has to lock it in order to ensure that the temporary pointer remains valid.
The only block-exit code in VB6 gets emitted at the exit of a procedure or object module.
-
Aug 7th, 2021, 12:13 PM
#7
Re: How do I properly exit a For-Next loop that contains a With block?
This is the oddest thing I have ever seen. Didn't even know about that. Always thought that With blocks were just syntactic sugar. This code actually produces an error:-
Code:
Private Type TEST
Value As Long
End Type
Private Sub Form_Load()
Dim a() As TEST
ReDim a(0 To 0)
With a(0)
.Value = 25
GoTo 20
End With
20:
ReDim a(0 To 1)
End Sub
I tested this exact same code in VB.Net and it worked as expected.
-
Aug 7th, 2021, 01:45 PM
#8
Re: How do I properly exit a For-Next loop that contains a With block?
Originally Posted by Niya
Always thought that With blocks were just syntactic sugar.
Nope. They are actually an optimization. One that can be significant if the object expression is complex enough (fetching an object that is a property of an object within a collection that is a property of another object for example).
-
Aug 7th, 2021, 02:03 PM
#9
Re: How do I properly exit a For-Next loop that contains a With block?
Originally Posted by dilettante
Nope. They are actually an optimization. One that can be significant if the object expression is complex enough (fetching an object that is a property of an object within a collection that is a property of another object for example).
Would this optimization happen be along the lines of not having to resolve the object expression every time when you use With blocks? I think Bonnie West mentioned something about this a few years ago. It has something to do with late binding I think.
-
Aug 7th, 2021, 02:09 PM
#10
Re: How do I properly exit a For-Next loop that contains a With block?
It's just a temporary reference. You can accomplish much the same by creating your own explicit temporary reference.
I'm not sure where late binding might come into it aside from the usual inefficiency, I'd have to think about that.
UDTs are different and a bit funky. You gain a lot less than a minor syntactic benefit. Unless the UDT is a member of an array, where With emits code to lock the array and End With unlocks it.
-
Aug 8th, 2021, 08:42 AM
#11
Re: How do I properly exit a For-Next loop that contains a With block?
I knew that With/End With blocks were more than syntactic sugar, as, at a minimum, when used with objects, they create a temporary object reference that's used in the block.
However, this use with arrays of UDTs presents an interesting got'cha.
I typically use With/End With with objects, rather than UDTs, so I started wondering if this same problem happened with an array of objects:
Code:
Option Explicit
Private Sub Form_Load()
Dim a() As New Collection
Dim n As Long
ReDim a(2)
For n = 0 To 2
With a(n)
.Add "asdf"
If .Count <> 0 Then Exit For
End With
Next n
ReDim a(0)
a(0).Add "asdf"
End Sub
Turns out, that code runs just fine.
Now sure, it's doing a lot of auto-instantiation, and the ReDim is un-instantiating those collections, but I don't really see how that makes any difference.
Last edited by Elroy; Aug 8th, 2021 at 09:38 AM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 8th, 2021, 09:13 AM
#12
Re: How do I properly exit a For-Next loop that contains a With block?
I was also curious if putting the UDT into a TypeLib would solve the problem, and it doesn't:
Code for Project2, with TypeLib named Project1.tlb:
Code:
Option Explicit
Private Sub Form_Load()
Dim a() As Project1.MyType
Dim n As Long
ReDim a(2)
a(0).Value = 1
a(1).Value = 2
a(2).Value = 3
For n = 0 To 1
With a(n)
If .Value = 2 Then Exit For
End With
Next n
ReDim a(0) ' Still errors, even when UDT is in a TypeLib.
a(0).Value = 123
End Sub
It appears that the problem is specific to arrays of UDTs, typelib or not.
Support shots:
Last edited by Elroy; Aug 8th, 2021 at 09:27 AM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 8th, 2021, 09:41 AM
#13
Re: How do I properly exit a For-Next loop that contains a With block?
Originally Posted by Elroy
Now sure, it's doing a lot of auto-instantiation, and the ReDim is un-instantiating those collections, but I don't really see how that makes any difference.
Pretty sure With blocks cause VB6 to call SafeArrayLock to lock the array.
-
Aug 8th, 2021, 09:52 AM
#14
Re: How do I properly exit a For-Next loop that contains a With block?
Originally Posted by Niya
Pretty sure With blocks cause VB6 to call SafeArrayLock to lock the array.
I would think so too, but why would they do that with arrays of UDTs and not arrays of objects. At that level, it would seem that an array is an array.
Also, the problem doesn't rear its head with an array nested in the UDT:
Code:
Option Explicit
Private Type MyType
Value() As Long
End Type
Private Sub Form_Load()
Dim a As MyType
Dim n As Long
ReDim a.Value(2)
a.Value(0) = 1
a.Value(1) = 2
a.Value(2) = 3
For n = 0 To 1
With a
If .Value(n) = 2 Then Exit For
End With
Next n
ReDim a.Value(0) ' Works just fine.
a.Value(0) = 123
End Sub
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 8th, 2021, 10:03 AM
#15
Re: How do I properly exit a For-Next loop that contains a With block?
An array is locked every time one if it elements is "alias" i.e. exist more than one "variable" (pointer in C nomenclature) which can access it.
Check this out:
Code:
Option Explicit
Private MyArray() As Long
Private Sub Form_Load()
ReDim MyArray(10) As Long
pvProcess MyArray(5)
End Sub
Private Sub pvProcess(Elem As Long)
ReDim MyArray(0 To 20) As Long '<--- fails with "This array is fixed or temporarily locked"
End Sub
While 5-th element is aliased inside pvProcess the MyArray must remain stable for the duration of the routine.
The parameter Elem is pointer in disguise. Changing it to ByVal removes aliasing, removes array lock and allows re-dimensioning the array.
Unfortunately UDTs cannot be passed ByVal and have to be manually copied to a local variable to emulate ByVal semantics.
cheers,
</wqw>
-
Aug 8th, 2021, 10:54 AM
#16
Re: How do I properly exit a For-Next loop that contains a With block?
Originally Posted by wqweto
An array is locked every time one if it elements is "alias" i.e. exist more than one "variable" (pointer in C nomenclature) which can access it.
Ok, that makes sense, but it still doesn't seem to be consistent.
I played around with it and figured out the following.
This code DOES fail on the ReDim MyArray(20):
Code:
Option Explicit
Private MyArray() As Collection
Private Sub Form_Load()
ReDim MyArray(10)
pvProcess MyArray(5)
End Sub
Private Sub pvProcess(Elem As Collection)
ReDim MyArray(20)
End Sub
However, this code DOES NOT fail on the ReDim MyArray(20):
Code:
Option Explicit
Private MyArray() As New Collection
Private Sub Form_Load()
ReDim MyArray(10)
pvProcess MyArray(5)
End Sub
Private Sub pvProcess(Elem As Collection)
ReDim MyArray(20)
End Sub
From the array's perspective, I'm not sure I see the difference. In both cases, I wouldn't think anything is instantiated (and I don't see how that even matters).
Last edited by Elroy; Aug 8th, 2021 at 10:58 AM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 8th, 2021, 11:04 AM
#17
Re: How do I properly exit a For-Next loop that contains a With block?
However, wqweto, you do bring up an important point. Very specifically, it has to be a specific element within the array that's aliased before this arises. And that's precisely what happens in the OP example (via a temp hidden variable within the With/End With block), and that's precisely what happens in your post #15 example. And, it explains why we need to "execute" the "End With" so that the array gets unlocked.
For the VB6 language to be a bit more complete, we need an "Exit With" statement.
That also explains why a nested array doesn't have this problem.
But my point in post #16 still doesn't make sense to me.
Last edited by Elroy; Aug 8th, 2021 at 11:17 AM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 8th, 2021, 11:29 AM
#18
Re: How do I properly exit a For-Next loop that contains a With block?
Originally Posted by Elroy
But my point in post #16 still doesn't make sense to me.
Even when an object is instantiated, it still doesn't fail:-
Code:
Option Explicit
Private MyArray() As New Collection
Private Sub Form_Load()
ReDim MyArray(10)
MyArray(5).Add 1
pvProcess MyArray(5)
End Sub
Private Sub pvProcess(Elem As Collection)
ReDim MyArray(0 To 20)
End Sub
This behavior is soooo inconsistent. I hate inconsistencies like that. It really drives me insane that you have to learn each and every quirk of this environment the hard way.
-
Aug 8th, 2021, 11:36 AM
#19
Re: How do I properly exit a For-Next loop that contains a With block?
Here's a partial explanation. When the "New" word is used, the array isn't locked when an element is aliased ... but, this just seems to be a compiler oversight:
Code:
Option Explicit
Private MyArray() As New Collection
Private Sub Form_Load()
ReDim MyArray(10)
MyArray(0).Add "asdf"
pvProcess MyArray(0)
End Sub
Private Sub pvProcess(Elem As Collection)
Debug.Print Elem.Item(1) ' Works as expected.
ReDim MyArray(20)
Debug.Print Elem.Item(1) ' IDE CRASH !!!!!!!!
End Sub
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 8th, 2021, 12:08 PM
#20
Re: How do I properly exit a For-Next loop that contains a With block?
Yea that's definitely a bug. By not locking the array, it allowed you to invalidate the pointer which is what the lock was suppose to prevent.
-
Aug 8th, 2021, 12:20 PM
#21
Re: How do I properly exit a For-Next loop that contains a With block?
A temporary reference to a "normal" object prevents that object's termination even if you crap on its reference in the array.
As far as I can tell, an instance of a UDT is just a dumb pointer to a BLOB, one that might have an IRecordInfo description in a referenced typelib or just in the IDE's symbol table. Whack that pointer, whack the data.
An object variable with As New semantics compiles to additional code with more complex actions each time your code "touches" it, to test whether it is Nothing and if so to create a new instance of the class..
-
Aug 9th, 2021, 03:07 AM
#22
Re: How do I properly exit a For-Next loop that contains a With block?
@Elroy: That's definately a compiler bug with "As New" arrays. The callsite neither locks the array, nor AddRefs the element passed because the reference parameter is ByRef (this is expected behavior for ByRef reference parameters).
The ReDim inside pvProcess succeeds which calls Release on each (live) element which deallocates the first element but its currently aliased by Elem parameter so we get a dangling pointer in Elem (ObjPtr to deallocated memory).
Ouch!
This problem stems from not locking the array. If non-"As New" arrays were not locked at callsite the same dire results would have happened with normal arrays too.
Using ByVal for the Elem parameter fixes the dangling pointer because ByVal reference parameters are (additionally) AddRefed/Released by the callsite, not to be able to deallocate the instance in the routine in similar side-effect interactions -- ReDim on array or Set to Nothing (i.e. interaction on variables in the outer scope).
Btw, this thread would be very instructive for TwinBasic test-suite on array locking at callsites or With statement for instance.
cheers,
</wqw>
-
Aug 9th, 2021, 10:49 AM
#23
Re: How do I properly exit a For-Next loop that contains a With block?
Just an FYI, I tried it all with a Class1 rather than using the Collection as my object, and everything works the same way:
If we start with Private MyArray() As Class1 then we get the Array Locked error message on the ReDim.
If we start with Private MyArray() As New Class1 then the ReDim executes, but trying to use an aliased array item crashes the IDE.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Aug 10th, 2021, 03:35 PM
#24
Re: How do I properly exit a For-Next loop that contains a With block?
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
|