Well, that's mighty civil of you. I just happen to have mucked around with computers for a while now and have picked up a few things.
Yes, I agree you cannot rely on the interval on a timer control as firing accurately, the only way to measure true elapsed time is, as you say, to get at the system clock. I used the Timer function in VB which returns milliseconds - I guess the QueryProgramCounter API may give you a finer resolution?
I am pretty sure that single threading is actually the cause of your problem. I think, when you hold down a command button, there is no "drag" element to the interaction so it doesn't set up a loop, so that's why it doesn't cause a problem. But in a single-threaded system, any loop by definition holds up any other pre-exisitng loop, because there is only one call stack.
I can demonstrate the single-threading nature of VB, by adapting the previous code as in the sample below. In the adaptation the application has a single form with just two buttons. When the button1 is pressed, the code enters a loop that only ends when you click on button2 - the Button1 loop waits for a semaphore set by Button2. The Button1 loop must contain DoEvents() to allow the Button2 event to register.
Now, here comes the interesting bit. As there is a DoEvents() call in the Button1 loop, what happens if instead of pressing Button2, you press on Button1 again? Well, that event is processed just like any other event, so it starts a completely new Button1 loop, also waiting for the flag to be set by Button2. Effectively, the DoEvents() call in the Button1 code sends the executing thread recursively back into the same Button1 code. You can press Button1 as many times as you like, and each time you get another recursive call and a new loop in the stack. None of the earlier loops in the stack can continue until all later loops have exited. So now, when you press on Button2, the final loop exits back to the previous loop, which also exits (because the flag was set by Button 2). The whole call stack unwinds in a rush, LIFO style. You could modify it to unwind just one loop at a time by resetting the semaphore at the exit point from the Button1 code.
Public Class Form1
Private bStopLoop As Boolean = False
Private iLoopCount As Long = 0
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim tt As String = "", tt2 As String = ""
Dim count As Long = 0
bStopLoop = False
iLoopCount = iLoopCount + 1
Dim MyLoopNumber As Long = iLoopCount
Do
tt2 = Format(Now(), "hhmmss")
If tt2 <> tt Then
count = count + 1
Debug.Print("Loop " & MyLoopNumber & ": " & count & " at " & tt2)
tt = tt2
End If
Application.DoEvents()
Loop Until bStopLoop = True
Debug.Print("Loop " & MyLoopNumber & " ended: " & count)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
'Stop loop button
bStopLoop = True
End Sub
End Class
Enter your message below
Sign in or Join us (it's free).