VBA: Jump to the beginning of a for loop - vba

I have a little problem with jumping back to the start of a for loop.
Part of the code:
For M = 1 To System
Openhours = Numberofhours(M, 1)
If Openhours = 0 Then
M = M + 1
Exit For
End If
I can have 1-6 "Systems", more could be added. "Openhours" says how many hours is dedicated to a particular system, but the problem is that if I happen to have a system (say system number 3) where there are no hours dedicated to at the moment, then the system crashes as, for example "Openhours" is zero with quite a few other arrays.
So I'd need to tell the code that immediately if it notices that Openhours is zero that it goes back to the beginning of the loop and tests the next system.
"Exit For" did of course not work as it skipped all the other systems after it found the first system with zero hours.

You could either check for Openhours being NOT equal to 0 and only execute your code in those cases, thus eliminating the need to jump to the next iteration or you could use the NEXT-keyword to jump to the next iteration of your loop.

Related

VBA - Leave Do Until when specific row is reached

In my VBA Code i go from the last row in my table to the top of my table. But i want that the Do Until Loop ends when it reached Row 10.
Right now I am using this Do Until Loop:
Do
' Do Something
Loop Until ActiveCell.Address = "$N$10:$BI$10"
How do i have to change my code that it will stop when it reached row 10?
Assuming rest of code is correct use:
Loop Until ActiveCell.Row = 10
If that fails you problem is logic in other parts of your code ie. ActiveCell never reaches row 10.
To answer your question in more theoretical fashion. There are multiple ways of exiting Do loops in VBA.
The proper way
1.1. While or Until at the begining
This checks the condition first, if the condition is met, it enters the loop and repeat with the condition being met at the start of every loop.
Do While i <= 5
'#code here
Loop
These two are equivalent.
Do Until i > 5
'#code here
Loop
1.2. While or until at the end
This is almost the same as what is described above. The only difference being, with sole Do statement at the beginning, your code-block always gets executed at least once! This can be particularly useful, when you want to execute something at least once, but don't want it to repeat unless a condition is met.
Do
'#code here
Loop While i <= 5
or
Do
'#code here
Loop Until i > 5
The enforced way
You can exit out of any loop, including Do with the so called Exit statement. This escapes the currently ongoing Do loop upon reaching the statement no questions asked. While you usually should try to avoid using the Exit statement, as in majority of cases it is possible to avoid using it with a proper condition at the While or Until portion of your code, it can come in handy in some cases.
Additionally, keep in mind, inside nested Do loops, Exit always exits only the innermost loop. This means, this would exit only the loop inside and let the others run, acting as a weird form of Continue
Do While (handler = True)
Do
'# execute me
If weird_condition = True Then
Exit Do
' i return to the "handler" loop
End If
field = field + 1
Loop Until field = field_amount
Loop
The not so nice enforced way
Alternatively, you can stop the entire exution, with the Stop statement. I would strongly advise against doing this, but technically it is a possibility so I'm listing it here. Similarly like End it ends the execution, but unlike End (eg. End Sub), it does not close any files or clear any variables - so technically this means you could use it to exit a loop. I would however recommend simply using the Exit statement instead. Can't really think of a case when I would ever use this.

Why do I get stack overflow BEFORE all the possible combinations have been reached?

So.
I am making a bruteforcer in visual basic.
It has a charset as shown below:
Dim charset as string
charset = "abcdefghijklmnopqrstuvwxyz1234567890."
There is 37 different chars right? The program is made to search for all the different combinations made with this charset, with a maximum of 3 different letters. For example
This is a combination that can be made: ac6
So since there is 37 letters and 3 slots the possible combinations are 37^3
But I wanted my program not to try the same combination twice.
So it saves every single combination tried in this location (Desktop)
Dim filex As System.IO.StreamWriter
filex = My.Computer.FileSystem.OpenTextFileWriter("c:\Users\" + Environment.UserName + "\desktop\alreadytested.txt", True)
filex.WriteLine(combination)
filex.Close()
And, at the start of the Sub that checks for new combinations, I have this
text = File.ReadAllText("c:\Users\" + Environment.UserName + "\desktop\alreadytested.txt")
index = text.IndexOf(combination) 'checks if it has been generated already
If index >= 0 Then
keyword() 'The sub
End If
But after some combinations (in this case the max 37^3 ~= 50.000 and I the program tried around 5200 times) I get this error
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
And this error points in this line of code
filex = My.Computer.FileSystem.OpenTextFileWriter("c:\Users\" + Environment.UserName + "\desktop\alreadytested.txt", True)
But why? at 5200 there is still 44800 possible random combinations, why do I get an overflow?
It would make sense if I got it when I had 50000 combinations out of 50000 possible tested, but now I have 10% only, so why do I get an overflow?
You keep on recursively calling the keyword() method. Every time you call a method its return address and possibly its arguments is/are added to the call stack. The callstack can only take a certain amount of calls before it overflows, and for your computer and that specific method of yours, that amount seems to be about 5200.
You should switch to using for example a While-loop instead, and whenever you want to block the rest of the execution and go back to the beginning of the loop you'd just call Continue While.
A little side note is also that you shouldn't open and close the file every time you read/write to it. Store the contents of the file in a long string (or even better, in a HashSet(Of T)) instead and check that every time you need to, then at the end of the loop you may write all the contents to a file.
If you still wish to write to the file during the process then do so. But instead open a stream before your loop which you keep writing to until the loop is finished, then close the stream.

How does VB.Net evaluate the end test of a FOR...NEXT loop?

I am teaching students about the FOR NEXT loop in VB.Net and I seem to be burdened with an understanding from learning Assembly language long ago. Trying to count the number of iterations that a line of code would do in several cases:
FOR x = 1 TO 1 (implied STEP 1)
(body)
NEXT x
Should do 1 loop, right? But I read the test as "until x is 1", so after the first loop, x becomes 2 and the test should not work.
How about: FOR x = 1 TO 1 STEP -1? Is your answer the same?
How about: FOR x = 1 TO 0 (implied STEP 1)? The body should never execute. But the test is "until x = 0", so it should cause an infinite loop as x climbs away from 0... Starting to see the issue?
How about: FOR x = 1 TO 0 STEP -1? Now it will do the body twice, right? But what is x at that point? -1. How did the test stop when it is one beyond what is says it will stop at?
I guess the compiler is actually testing until x = (endval) + stepval. I can visualize that in Assembler, but otherwise I confess that I can't see or explain to my students how this all actually works the way it is assumed it "should". (This question seems trivial with constants like "1", but imagine variables or other ways of creating the FOR loop parameters.) Can anyone shed some light? Thank you.
It seems Tony Hinkle provided the answer, but did so as a comment. So I'm providing the particular text he mentioned here, as an answer:
"When a For...Next loop starts, Visual Basic evaluates start, end, and step. Visual Basic evaluates these values only at this time and then assigns start to counter. Before the statement block runs, Visual Basic compares counter to end. If counter is already larger than the end value (or smaller if step is negative), the For loop ends and control passes to the statement that follows the Next statement. Otherwise, the statement block runs.
Each time Visual Basic encounters the Next statement, it increments counter by step and returns to the For statement. Again it compares counter to end, and again it either runs the block or exits the loop, depending on the result. This process continues until counter passes end or an Exit For statement is encountered.
The loop doesn't stop until counter has passed end. If counter is equal to end, the loop continues. The comparison that determines whether to run the block is counter <= end if step is positive and counter >= end if step is negative."
To help clarify:
FOR x = 1 TO 1 (implied STEP 1) will loop 1 time.
FOR x = 1 TO 1 STEP -1 will loop 1 time.
FOR x = 1 TO 0 (implied STEP 1) will never loop because counter is already past end.
FOR x = 1 TO 0 STEP -1 will loop twice

Code Execution Sequence

I am a PLC programmer who is currently using a variant of VB to control a motor.
I want to call a function that will execute moves and not return to the main code until the move has been completed. Currently here is what I have:
Program 'Main Program
While 1
If move_req = 1
Function MoveMotor
End If
Wend
End Program
Function MoveMotor
MoveABS 10 ' Move to encoder position 10mm
move_complete = 1
While move_req = 1
'Do Nothing
Wend
End Function
For some reason this code isn't working and the move command is being sent over and over again. Could this be because the main program continues to run when the function is running? Is that how VB works? I am used to thinking of code sequence in terms of PLC's where they scan through everything repeatedly at a certain frequency.
Whenever the move is complete, there must be a way for it to be detected by the program. It looks like you want move_req to be set to zero when this happens, but I cannot see what would cause that. How does the machine signal the program that it's finished moving?
A second point is that when you have a loop that waits while it checks for variable change, it can cause a CPU spike. You can put a pause in the loop with some thing like System.Threading.Thread.Sleep(100) where 100 is milliseconds to pause.

VB spread sheet writing lock

at the moment I have a small application and need to take information from an object and display it into an excel file, using the Microsoft.office.interop class I've been able to write to the file, and it shows one by one the records being added, however about once every 3 times I try it, the spreadsheet stops filling somewhere between the 300th and 600th record, I have 6,000 in total and it's not breaking every time, I put a check after it finishes to see whether the last record is filled in but the code never reaches that point and I'm unsure of what's happening
I also don't know how to debug the problem as it'd mean going through 6,000 loops to check for it stopping... which might not even happen?
a little section of the code is here
loadExcel(incidents, WorkSheetName)
If WorkSheetName.Cells(DBObject.HighestInci + 1, 6) Is Nothing Then
MessageBox.Show("Failed to fill spreadsheet, Retrying now.")
loadExcel(incidents, WorkSheetName)
End If
above is the code calling and checking the method below
Private Sub loadExcel(ByVal incidents As List(Of Incident), ByRef WorkSheetName As Excel.Worksheet)
Dim i = 2
For Each inc As Incident In incidents
WorkSheetName.Cells(i, 1) = inc.DateLogged
WorkSheetName.Cells(i, 2) = inc.DateClosed
WorkSheetName.Cells(i, 3) = Convert.ToString(inc.DateLogged).Substring(3, 2)
i += 1
Next
End Sub
Thanks in advance
EDIT
I'm thinking loading it to a buffer of some sort then writing once they have all been updated would be the way to go instead of it currently loading and writing each separately? however I have no idea where to start for that?
I've fixed my problem, with what I had above Excel was opened and it started printing into the spreadsheet line by line, the problem is that any interactions with excel would cause the process to freeze
By adding an
ExcelApp.visible = false
before carrying out the process and an
ExcelApp.visible = true
afterwards, it all works and then opens the file afterwards