automatically "killTimer" when script is manually terminated using the VBE "reset" button - vba

After I set a timer with SetTimer (lib "user32"), it must be killed with KillTimer before ending the script, otherwise it causes weird behaviors like closing the intellisense menu every time the (ghost) timer fires.
However, some times when in an emergency situation I must stop the script via the "reset" button of the VBE and this causes the timer to remain unkilled, with the consequent weird behaviours.
Is there a way to automatically run a KillTimer when I press the "reset" button?

The timer continues in the application, separate from any single VB Project. But event scripts must be part of a VB Project. If you press the reset button, I don't imagine that it will be able to trigger an event script in the project that was just reset.
Instead, you can use the timer's script to track the project session and check to see if the session has been reset.
Here's a simplified example, using Application.OnTime instead of SetTimer:
Dim eventTime As Date
Sub StartTimer()
timer start:=True
End Sub
Sub timer(Optional start As Boolean = False)
If eventTime = 0 And Not start Then Exit Sub
Debug.Print Now
eventTime = Now + TimeValue("00:00:02")
Application.OnTime eventTime, "Module1.Timer"
End Sub
The OnTime loop continues even if you press the Reset Button, but the reset button clears the value in eventTime so the next time the timer script runs, eventTime = 0 is true and the OnTime loop is exited.

Related

In Excel vba, how to achieve timer function

What I thought would be quick excel vba, doesn't seem to be quick anymore. Basically, i want to loop through a set of questions within a given time. when the given time has expired or all questions completed, exit the loop (close the form and return). BTW, would like to show a timer countdown on the form as well. is it possible to achieve it?
I can get a form to show questions with answer options but not sure how to add the time criteria.
As far I know, this is not an ease task to achieve. Briefly speaking, one of the possible ways to do it is:
1. write your own class module for this program
2. create a form with questions, through the form constructor (Form_Initialize) create a new instance of your class (from point 1.) and link it to the form through variables, in addition your form needs to have variables to pass the information supplied by user at a later stage (your questions) to the object behind for storing
3. when your class is instantiated, the object will have a piece of code that contains a timer, and this timer will fire events, let's say, every second to refresh your form (to show how much time is left for answering questions), see UserForm.Repaint method. The questions that have been answered so far would be re-inserted into the form on repaint (info should be stored in your class object, then re-used to update the form through timer events).
Check this link as a starting point:
https://msdn.microsoft.com/en-us/library/office/gg264327.aspx
or search for: RaiseEvent Statement in Excel Help (in VB Editor).
To see how to create vba classes:
http://www.cimaware.com/resources/article_39.html
In general, you need to search for information on how to:
create class modules,
separate object model (instantiated class) from the form,
pass information between the form and the model,
make the model to repaint your form at regular intervals (firing events)
I think Application.OnTime method may can help.
Scheduling Events With OnTime And Windows Timers
---updated---
As requested in comment section, I include the essential part on how the function works
'variable to keep time interval the timer run again
Public RunWhen As Double
'main function that kick off the exam
Public Sub startExam()
'a label display how much time left, says start with 60 seconds
DisplayTime.Caption = "60"
'call a function that load and display question on screen
'assume call LoadQuestion again when user provided quiz answer, not implemented
LoadQuestion (1)
'start the
call Timer()
End Sub
'the timer function repeat itself
Sub Timer()
'set the next run time for Timer function
RunWhen = Now + TimeSerial(0, 0, 1)
'set the schedule
Application.OnTime EarliestTime:=RunWhen, Procedure:="Timer", Schedule:=True
'update the time left on screen
UserForm1.DisplayTime.Caption = UserForm1.DisplayTime.Caption - 1
'if the time deduced to 0, stop the schedule and alert user
If UserForm1.DisplayTime.Caption = "0" Then
Application.OnTime EarliestTime:=RunWhen, Procedure:="Timer", chedule:=False
MsgBox "TimesUp"
End If
End Sub

Threading. How does button click interrupt/influence program flow

My project is vb.net 2010 windows desktop form.
So far, single threaded (default).
If a SUBroutine has a for...next loop in it that is running, what happens if a buttonclick event is fired and within that event a variable is changed? Like: does program execution leave the loop that was running? Or does it continue to run while that variable is changed by the buttonclick event?
What I'm aiming for:
If someone clicks the button, blnRequestStop is set to True.
Within that for...next loop, just before the "next" it checks blnRequestStop. If true then it will exit the "for" loop.
I'm guessing I need to use threads? Can anyone give me a simple example, please?
EDIT:
This code below seems to be working fine. But maybe you all see a problem?
If (btnProcess.Text = "Done!") Then
End
ElseIf (btnProcess.Text = "IMPORT") Then
bRequestStop = False
t1 = New Thread(AddressOf ProcessDo)
t1.Start()
Else
t2 = New Thread(AddressOf MyInterrupt)
t2.Start()
End If
Here is the short version of what ProcessDo and MyInterrupt do:
Private Sub ProcessDo()
For each X in blahblah
'do stuff (yes, includes interface)
if (blnInterrupt) then exit For
Next X
End
End Sub
Private Sub MyInterrupt()
blnInterrupt=true
End Sub
Yes, you probably want to do the long-running task on a background thread. Here's a code sample including how you'd get the results back to the UI thread when you're done (otherwise you'll get errors about Cross-thread operation not valid).
ThreadPool is a nice way to do some work on a background thread. You could set stopIt = True in the button click event for the stop button.
ThreadPool.QueueUserWorkItem(
Sub()
For Each thing In things
If stopIt Then Exit Sub
'Do the stuff!
Next
'We're done, update UI
Me.UpdateUI("All done!")
End Sub)
To safely update the UI, you'll need to make sure you get back to the UI thread.
Public Sub UpdateUI(result As String)
If Me.InvokeRequired() Then
'If we aren't on the UI thread, invoke this function on the UI thread
Me.BeginInvoke(Sub() UpdateUI(result))
Exit Sub
End If
'Update UI here
lblResult.Text = result
End Sub
Execution is 'stopped' (in a way) until the Loop finishes him job, so yes, you need to multi-thread.
It it is not a very long operation where you need to update UI controls during the Loop then you just can use Application.DoEvents inside the loop to be able to use other controls as normally in the application when FOR loop is working, but I advise you that this will have a negative impact on UI performance, but if it's not a long duration loop then you maybe would consider to use DoEvents instead introduce into multi-threading it's just an alternative, not recommended but, you can use it.
PS: Forgive my English

VBA Access 2003 Only Allow Press Button Once Within Reasonable Amount Of Time

I have a button on a form that takes the RowSource from a listbox on my form, creates an Excel, Worksheet and Query Table object, queries the information into excel and then formats it all. The problem is that if the user presses the mouse button quickly, he or she will activate this button just as many times.
I have attempted putting the code to sleep in-between, but this just causes each button press to last as long as the sleep and then however long the query takes. What I used:
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
I also tried setting the focus to another control and disabling the button, but I'm not quite sure how to re-enable it after a set amount of time, because even putting it at the end of the sub, Access still executes the button for every button press .
Any suggestions?
Instead of waiting a fixed amount of time, disable the command button when first clicked. Then do your long-running operations, and enable the command button again afterward.
Assuming the name of the command button is cmdToExcel ...
Private Sub cmdToExcel_Click()
Me.txtNote.SetFocus
Me.cmdToExcel.Enabled = False
' replace next line
MsgBox "call long running code here"
Me.cmdToExcel.Enabled = True
Me.cmdToExcel.SetFocus
End Sub
Note you must SetFocus to a different control before disabling the command button because Access won't allow you to disable it while it still has focus.
You may not want to SetFocus back to the command button at the end --- I just made a wild guess on that one. :-)
It seems that code flow control returns to the click event procedure before the long running process has finished. So the command button is enabled again prematurely.
In that case you can insert a pause, using your Sleep API declaration, after calling long running code.
Private Sub cmdToExcel_Click()
Me.txtNote.SetFocus
Me.cmdToExcel.Enabled = False
' replace next line
MsgBox "call long running code here"
Sleep 5000 ' 5 seconds
Me.cmdToExcel.Enabled = True
Me.cmdToExcel.SetFocus
End Sub
I realize now that is what you asked for in the first place. Sorry I was thick.
Say you want the button to be disabled for no less than 5 seconds
Me.cmdButton.Enabled = False
timing = Dbl(Now())
'
' query code
'
timing = Dbl(Now()) - timing
seconds = Int(CSng(timing * 24 * 3600))
If seconds < 5 Then
Sleep (5 - seconds) * 1000
End If
Me.cmdButton.Enabled = True

What Would Cause A Form To Freeze Upon Executing Code

I'm trying to figure why my form freezes up when executing some code. I also can't minimize or move the form. Is it because of the WaitForExit being used in the process?
The below code is tied to a button click.
If Checkbox1.checked = True Then
Call Test()
End If
If Checkbox2.checked = True Then
Goto NextStep
Else
Goto StopProcessing
End If
Here is the test sub I'm calling. Calls an exe with an optional argument.
Using psinfo As New Process
psinfo.StartInfo.FileName = "C:\Temp\Test.exe "
psinfo.StartInfo.Arguments = Arg1
psinfo.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
psinfo.Start()
psinfo.WaitForExit()
End Using
The WaitForExit was added (so I thought) to not process the next statement (next statement being the If statement for Checkbox2) until the process was complete. Is this not the case?
The WaitForExit was added (so I thought) to not process the next statement (next statement being the If statement for Checkbox2) until the process was complete.
When you call WaitForExit, it will block until the process (Test.exe) completes.
Since you're running this on the user interface thread, it will cause your form to "freeze" until the process completes fully.
If you need this to not occur, you would need to wait on a background thread. You could, potentially, move this code into a BackgroundWorker and use it to synchronize with your main window - but you will need to handle "waiting" for the process to finish in a different manner (ie: disable your UI up front, run the process, re-enable when complete).
Note that, with the Process class, another alternative would be to add EnableRaisingEvents on the process, then adding a handler to Process.Exited. This will let you not WaitForExit(), but instead get notified via an event when the process completes.

Constantly updating "Status Form" locks over time

This is my first time using VBA in Outlook so please bear with me
I've created a basic Macro that does various things to folders. Since this takes a while I decided to make a status window that says what its currently doing. I simply keep setting one of the label's values with this
Function UpdateStatus(Message As String)
StatusForm.StatusUpdate.Caption = Message
StatusForm.Repaint
End Function
The issue is that after it runs for a bit (5-15 seconds) the window and the rest of outlook locks; the form no longer updates and has a "(Not Responding)" in its window title.
I feel like I'm somehow dead locking the UI thread but I'm at a loss on how to work around it. Commenting out Repaint not surprisingly doesn't let it update at all, but outside of that I don't know where to look
Any suggestions?
Try adding DoEvents in your computationally intensive loop. This yields the executing code to the UI thread so that other things can get done when you have computationally intensive stuff going on in the background. Office is single-threaded, so you can block the UI when you are running macros.
Sample:
Sub LockUI()
Dim x
x = Timer
Do While Timer - x < 5
'Blocks the UI for 5 seconds
Loop
End Sub
Sub LockUI2()
Dim x
x = Timer
Do While Timer - x < 5
'Doesn't block the UI
DoEvents
Loop
End Sub