Results 1 to 24 of 24

Thread: How do I properly exit a For-Next loop that contains a With block?

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Oct 2008
    Posts
    1,181

    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

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    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:
    1. With a(n)
    2.     If .Value = 2 Then Exit For
    3. End With
    to this:
    vb Code:
    1. 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.

  3. #3
    Addicted Member gilman's Avatar
    Join Date
    Jan 2017
    Location
    Bilbao
    Posts
    178

    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

  4. #4
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    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>

  5. #5
    PowerPoster
    Join Date
    Feb 2017
    Posts
    5,067

    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.

  6. #6
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    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.

  7. #7
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,601

    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.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  8. #8
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: How do I properly exit a For-Next loop that contains a With block?

    Quote Originally Posted by Niya View Post
    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).

  9. #9
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,601

    Re: How do I properly exit a For-Next loop that contains a With block?

    Quote Originally Posted by dilettante View Post
    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.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  10. #10
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    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.

  11. #11
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    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.

  12. #12
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    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:

    Name:  tlb.jpg
Views: 298
Size:  49.5 KB

    Name:  obj.png
Views: 319
Size:  11.9 KB
    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.

  13. #13
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,601

    Re: How do I properly exit a For-Next loop that contains a With block?

    Quote Originally Posted by Elroy View Post
    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.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  14. #14
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    Re: How do I properly exit a For-Next loop that contains a With block?

    Quote Originally Posted by Niya View Post
    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.

  15. #15
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    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>

  16. #16
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    Re: How do I properly exit a For-Next loop that contains a With block?

    Quote Originally Posted by wqweto View Post
    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.

  17. #17
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    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.

  18. #18
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,601

    Re: How do I properly exit a For-Next loop that contains a With block?

    Quote Originally Posted by Elroy View Post
    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.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  19. #19
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    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.

  20. #20
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,601

    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.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  21. #21
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    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..

  22. #22
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    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>

  23. #23
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    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.

  24. #24

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width