Attribute VB_Name = "APITimer"
Option Explicit                                            '-©Rd-

' =================================================================
'                    Simple (imprecise) Timer
' =================================================================
' This API Timer is intended to replace the VB Timer Control.
'
' API timers don’t need to be explicitly terminated at application
' shutdown as all timers associated with a window are destroyed
' automatically as part of the process of destroying the window.
'
' However, termination of all running timers with an EndTimer call
' during application shutdown is recommended.
'
' You shouldn’t rely on the timer to be extremely accurate, as it’s
' subject to the unpredictability of Win32 thread scheduling.
' =================================================================

' System Timer API’s
Private Declare Function InitTimer Lib "user32" Alias "SetTimer" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapseMilliseconds 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 Const FAILED = 0&

Private m_TimerId As Long
Private m_hWnd As Long

' ================================================================
'                            SetTimer
' ================================================================
' SetTimer creates a new timer with the specified interval.
'
' A valid window handle for the current process is required.
'
' You can optionally specify a non-zero timer ID to be used, which
' will also be returned by SetTimer on success. This feature may
' prove convenient when processing the timer event by allowing the
' use of predefined ID values (to correspond to a select case
' statement, for example).
'
' The timer ID parameter allows the ability to cancel a running
' timer during the fire event but still allows the same ID to be
' re-assigned with another call to SetTimer at the end of the
' timer event.
'
' This feature also allows you to be able to modify the interval
' of a running timer by passing the existing timer’s ID with the
' new millisecond value without the need to end the current timer
' and then begin a new timer.
'
' Specifying a millisecond interval of less than one terminates
' the timer specified by the ID parameter - no error is raised
' if the ID parameter is ommited or invalid.
'
' API timers are a reasonably limited resource. If all timers
' are in use, the SetTimer function fails.

' SetTimer returns the timer’s ID on success, and returns zero
' on failure; no error is raised.
' ================================================================
Public Function SetTimer(ByVal Owner_hWnd As Long, ByVal Milliseconds As Long, Optional ByVal TimerId As Long) As Long
    m_hWnd = Owner_hWnd
    If (Milliseconds > 0&) Then
        If (TimerId = 0&) Then
            If (m_TimerId = 0&) Then m_TimerId = RandomNeg(True)
            m_TimerId = m_TimerId + 1&
            TimerId = m_TimerId
        End If
        If (InitTimer(m_hWnd, TimerId, Milliseconds, AddressOf TimerProc) <> FAILED) Then
            SetTimer = TimerId
        End If
    Else
        If (KillTimer(m_hWnd, TimerId) <> FAILED) Then SetTimer = TimerId
    End If
End Function

' ================================================================
'                            EndTimer
' ================================================================
' EndTimer terminates the timer specified by the timer ID.
'
' Running timers can be terminated using EndTimer, or by passing
' SetTimer a millisecond interval of less than one and passing the
' timers ID.
'
' This function returns True on success, or False otherwise.
' A False return value indicates that the timer specified by the
' ID parameter was not a valid running timer; no error is raised.
' ================================================================
Public Function EndTimer(ByVal TimerId As Long) As Boolean
    EndTimer = (KillTimer(m_hWnd, TimerId) <> FAILED)
End Function

' =================================================================
'                       TimerProc Callback
' ==================================================================
' Parameter Description
' --------- -----------
' hWnd      The window handle that was passed as the hWnd parameter
'           to the SetTimer function. Because Windows never looks at
'           this parameter, you can pass a NULL here if the callback
'           doesn't need an hWnd or if no hWnd is available at the
'           time SetTimer is called.
'
' wMsg      Always WM_TIMER.
'
' nIDEvent  The timer’s ID.
'
' dwTimer   DispatchMessage calls the GetTickCount function and then
'           passes the value obtained in dwTimer.
' ==================================================================
' After the specified time has elapsed, the system calls the
' TimerProc sub. You must add code to handle this event.
'
' Each running timer calls this same TimerProc sub-routine, and so
' each TimerProc event is identified by the idEvent parameter which
' matches the ID returned by SetTimer.
'
' It is important to note that the use of static and module level
' variables within this sub or your forms timer fire event would be
' susceptible to erroneous results as multiple timer events can fire
' with the use of DoEvents, displaying message boxes and other user
' interaction during any given ID fire event.
' =================================================================
Private Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long)

'' Add the Timer_Fire sub-routine to your form:
'Public Sub Timer_Fire(ByVal TimerId As Long)
'    Debug.Print "TimerId = " & TimerId
'    'EndTimer TimerId
'End Sub

frmZipSearch.Timer_Fire idEvent ', dwTime

'Debug.Print "The timer with ID " & idEvent & " just fired. System time is " & dwTime
End Sub

' ==================================================================
'                             Details
' ==================================================================
' When a timer is created, it is assigned an ID. Timer IDs are passed
' to timer callback functions and with WM_TIMER messages. Timer IDs
' are also used when killing the timer with the KillTimer function.
'
' The nIDEvent parameter lets an application choose the timer’s ID
' if the hWnd parameter is not NULL. If the hWnd parameter is NULL,
' Windows chooses an ID and returns it from the SetTimer function.
' In this case, the nIDEvent parameter is ignored, regardless of
' the value of lpTimerFunc.
'
' The return value from SetTimer is 0 (zero) if the timer could not
' be created. This situation occurs only when all timers are in use
' before the function is called. SetTimer returns a nonzero value
' if the timer is successfully created. If the hWnd parameter is
' NULL, this nonzero value is the timer’s ID. If hWnd is nonzero,
' the function succeeded, and the timer’s ID is the value passed
' in nIDEvent. The timer’s ID in nIDEvent must be saved because
' it will be needed later to kill the timer.
'
' Tip: If the application calls SetTimer with a nonzero hWnd, it
' must specify the timer ID. Contrary to the Windows version 3.0
' documentation, this ID, not the SetTimer return value, must be
' used when killing the timer.
' ==================================================================

Public Function RandomNeg(Optional ByVal bRandomize As Boolean) As Long
    If bRandomize Then Randomize
    RandomNeg = CLng((2147483600# * Rnd) - 2147483648#) '2147483647# * Rnd
End Function
