Time delays and timed events:   Return to List

The most efficient way to perform time delays (or timed events) is with a callback
from the system -- as opposed to creating loops in programs (DoEvents notwithstanding!)
-- because of the system resources tied up in executing the loop.

There are 2 common ways to produce callbacks -- one is by using the VB Timer Control
(from the VB toolbox). The Timer Control can be used by dragging and dropping on a
form in VB or VBA (note -- you can always set a form's visibility to zero if you don't
wish the user to see it).

When you double-click the Timer Control is will create a skeleton procedure that will
Fire (i.e., be executed) at each Timer interval. The interval is a property set in the
properties window -- and the number is in milliseconds. So if you put an interval of
15000 the Timer procedure will execute every 15 seconds. Or said another way -- at each
timer interval the system will callback to this procedure -- thereby executing it.

Also, the Timer interval's limit is 65000 (65 seconds). If you wish to have a longer
time interval (30 minutes, for instance), just put a counter in the Timer procedure,
and perform whatever other process you wish when the count reaches a certain level,
then reset the counter for the next interval.

' Example: You want to execute a macro every 30 minutes. You would:

(1) Drag & drop a timer control on a form.
(2) Set the timer control interval to 15000 (it will fire every 15 seconds).
(3) Create a global counter variable OUTSIDE of the Timer Control, such as in the
declaration area of the form: Dim gintTimerCounter as Integer
(4) Double-click the Timer control to create the skeleton procedure.
(5) Inside the procedure you'll add 1 to the global variable each time it executes:
       gintTimerCounter = gintTimerCounter + 1
(6) If gintTimerCounter = 120 Then
       Application.Run MacroName
       gintTimerCounter = 0' to reset
    End If

' Note: 2 things will improve the stability of the timer control:
(1) Placing it in a form that is opened/closed the least amount of times, and
(2) Rather than constantly enabling/disabling the control -- just let it run and zero your counter when you wish to begin timing a particular process. [So you can have a Timer control set to an interval of 10000 (fires every 10 seconds), and depending on the number of counters across your application, use the same Timer for the timing of multiple processes.]

The alternate method of achieving a system callback (recommend that you also don't exceed 65000 for your interval -- though the system API function won't produce an error) is to use the AddressOf property of either a module or a form.

In the declaration portion of either a module or form,
    you would put the API declarations:


Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, _
            ByVal nIDEvent As Long, _
            ByVal uElapse As Long, _
            ByVal lpTimerFunc As Long) As Long

Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, _
            ByVal nIDEvent As Long) As Long

Private mlngTimerID As Long' Since there can be numerous timers running,
            ' you need to know the ID for when you wish to STOP it.

' Module code
Public Sub StartTimer(lngInterval As Long)
' lngInterval would be something as before (example: 40000 for 40 second interval)
    mlngTimerID = SetTimer(0, 0, lngInterval, AddressOf TimerCallBack)
End Sub

' NOTE: TimerCallback could be ANY procedure within a module or form !!
Public Sub TimerCallBack()
    ' Do Something Here
End Sub

Public Sub StopTimer()' You probably don't want the timer to run indefinitely.
    KillTimer 0, mlngTimerID
End Sub

So you use one module the StartTimer module -- to start the timing, and when the callback
occurs (after the interval has lapsed) -- whatever AddressOf was passed will get executed.




Note to Webmaster