Visual Basic Code of the Week (COTW)
http://www.codeoftheweek.com
Issue #48
Online Version at http://www.codeoftheweek.com/membersonly/bi/0048.html (paid subscribers only)
All content and source code is Copyright (c) 1998 by C&D Programming Corp. No part of this issue can be reprinted or distributed in any manner without express written permission of C&D Programming Corp.

Notes about this ezine

If you are not a paid subscriber, you must have signed up for our free trial at http://www.codeoftheweek.com. Our ezine is not an unsolicited message (in other words a spam email). Keep in mind that if you signed up for our free trial you can still receive a total of four issues at no cost to you. After you receive the four issues you will be notified about continuing your subscription.

If you do not wish to continue to receive this ezine, please email us at cancel@codeoftheweek.com

Requirements for this Issue

The source code in this issue is designed for Visual Basic 5.0 and above. This is due to the use of the new AddressOf feature of VB 5. AddressOf allows you to pass the address of a subroutine created in a code module.

If you have any questions about this issue, please email us at questions@codeoftheweek.com

In this Issue

This issue introduces a Timer class to be used without any forms or controls. It is designed to be as close to the built-in control Timer that VB includes. It uses two API calls (SetTimer and KillTimer) to accomplish this task.

cTimer

The methods/properties in this class will be shown below. There are two properties and one method you need to be aware of.

Public Property Let Interval(lInterval As Long)
Public Property Get Interval() As Long

The Interval property sets the timer interval in milliseconds. For example, if you want a the timer to fire every 10 seconds, you would set Interval to 10,000. If a timer is currently active, it will disable the current timer and start a new timer with the updated Interval

Public Sub CallBackProc(lCallBackProc As Long)

The CallBackProc is the subroutine that the timer will call when it ticks off each Interval. We would have liked this to be a property, but the AddressOf keyword does not seem to be supported in a property statement. The subroutine you declare as your callback routine must be in a standard code module and have the following declaration:

Private Sub SampleTimerProc(ByVal hwnd As Long, _
                            ByVal uMsg As Long, _
                            ByVal idEvent As Long, _
                            ByVal dwTime As Long)
    ' Do your stuff here
End Sub

You can use the dwTime to determine exactly how much time has elapsed between each timer tick. This number is also in milliseconds. You should ignore the other parameters for this class module (If you want more information, check out the documentation for SetTimer).

Public Property Let Enabled(bEnabled As Boolean)
Public Property Get Enabled() As Boolean

The enabled property works just like you think it would. If you set Enabled = True the timer will start ticking. If you set it to False, it will stop. If any errors occur here, they will be raised. If an error occurs starting or stopping the timer, an error number of 1 will be raised with an appropriate description.

Sample Usage

This example shows how to use the cTimer class.

In a Code Module we declared this:

Public Sub MyTimerCallback(ByVal hwnd As Long, _
                     ByVal uMsg As Long, _
                     ByVal idEvent As Long, _
                     ByVal dwTime As Long)
    Form1.Label1 = Val(Form1.Label1) + 1
End Sub

On Form1 we created three buttons (cmdStart, cmdStop, cmdChange) and a text box (txtTimer).

Dim oTimer As New cTimer

Private Sub cmdStart_Click()
    ' Start up the timer
    oTimer.CallBackProc AddressOf MyTimerCallback
    oTimer.Interval = Val(txtTimer.Text)
    oTimer.Enabled = True
End Sub

Private Sub cmdStop_Click()
    ' Stop the timer
    oTimer.Enabled = False
End Sub

Private Sub cmdChange_Click()
    ' Change the interval on the timer
    oTimer.Interval = Val(txtTimer.Text)
End Sub

Source Code

Just paste this source code into a class module called cTimer and include it in your project.

'----------------------------------------------------------------------
'
'   Class Name:     cTimer
'   Written By:     C&D Programming Corp.
'   Create Date:    6/98
'   Copyright:      Copyright 1998 by C&D Programming Corp.  Source
'                   code may not be reproduced except for use in a
'                   compiled executable.  All rights reserved.  If
'                   you would like to reprint any or all of this
'                   code please email us at info@codeoftheweek.com
'----------------------------------------------------------------------
Option Explicit

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 mlCallBackProc As Long     ' The address of the call back procedure to use

Private mlInterval As Long      ' Interval to use for the timer
Private mlTimerID As Long       ' Internal timer id, 0 when timer is disabled

'
'   This had to be treated a little different because VB does not seem to
'   support passing the AddressOf a function as a property value.  So, we
'   just "almost" simulate it by creating a subroutine to get the value.
'
Public Sub CallBackProc(lCallBackProc As Long)
    mlCallBackProc = lCallBackProc
End Sub

Public Property Let Interval(lInterval As Long)
    On Error GoTo Handler

    mlInterval = lInterval
    If Enabled Then     ' If it currently ticking, update the timer.
        Enabled = False ' stop timer
        Enabled = True  ' restart timer
    End If
    Exit Property

Handler:
    Err.Raise Err.Number, Err.Source, Err.Description
End Property

Public Property Get Interval() As Long
    Interval = mlInterval
End Property

Public Property Get Enabled() As Boolean
    Enabled = (mlTimerID <> 0)
End Property

Public Property Let Enabled(bEnabled As Boolean)

    On Error GoTo Handler

    If bEnabled Then    ' if we want to turn it on...
        If Enabled Then ' if it is already ticking, turn it off
            StopTimer mlTimerID
        End If
        mlTimerID = StartTimer(Interval, mlCallBackProc)
    Else                ' we want to turn it off
        If Enabled Then ' if it is not already ticking, skip the stop
            StopTimer mlTimerID
            mlTimerID = 0   ' makes this timer disabled (see Enabled Get property)
        End If
    End If
    Exit Property

Handler:
    Err.Raise Err.Number, Err.Source, Err.Description
End Property

Private Sub Class_Terminate()
    Enabled = False     ' make sure we disable this timer on close!
End Sub

'
'   This routine starts a system level timer and calls the callback routine
'   you pass as TimeCallback.  A sample call might look something like this:
'
'   StartTimer 1000, MyCallBack
'
'   MyCallBack will need to be defined like the SampleTimerProc is shown
'   below
'
'   The Interval is in milliseconds and can be as large as 2,147,483,647, which
'   is many days.
'
'
Private Function StartTimer(ByVal Interval As Long, TimerCallback As Long) As Long
    Dim lTimerID As Long

    lTimerID = SetTimer(0, 0, Interval, TimerCallback)
    If lTimerID = 0 Then
        Err.Raise 1, "StartTimer", "Could not start the timer."
    Else
        StartTimer = lTimerID
    End If
End Function

'
'   This routine will stop a timer that was started with StartTimer.  It is
'   very critical that you stop any timer before your program ends.  If you
'   do not stop the timer, you risk crashing Visual Basic or your application.
'
'
Private Sub StopTimer(TimerID As Long)
    Dim lTimerID As Long

    lTimerID = KillTimer(0, TimerID)
    If lTimerID = 0 Then
        Err.Raise 1, "StopTimer", "Could not stop the timer."
    End If
End Sub

'
'   This is a sample callback procedure
'
Private Sub SampleTimerProc(ByVal hwnd As Long, _
                            ByVal uMsg As Long, _
                            ByVal idEvent As Long, _
                            ByVal dwTime As Long)

    ' Do your stuff here

End Sub

This document is available on the web

Paid subscribers can view this issue in HTML format. There is no additional source or information in the HTML formatted document. It just looks a little better since we have included some HTML formatting. Just point your browser to link at the top of this document.

Summary

That concludes this issue of COTW. We hope you find the source code useful in your development.

The below describes the ways you can supply us some feedback about COTW. We would like to see our members help mold COTW into the best Visual Basic source code resource available. But to do that we need your feedback about what you like and what you do not like about COTW.

Advertising

If you are interested in advertising in COTW please email us at sponsor@codeoftheweek.com Our rates are VERY reasonable, actually they are almost FREE. We reach over three thousand Visual Basic developers each week.

How to tell us what you think

If you have any suggestions for topics you would like to see covered or questions about this issue, please email them to info@codeoftheweek.com or use online feedback form at http://www.codeoftheweek.com/feedback.html.

If you have any source code you would like to submit for possible inclusion in COTW, please fill out our online submission form at http://www.codeoftheweek.com/submission.html.

Contact Information

C&D Programming Corp.
PO Box 20128
Floral Park, NY 11002-0128
Phone or Fax: (212) 504-7945
Email: info@codeoftheweek.com
Web: http://www.codeoftheweek.com

Subscription Update

Thank you for trying Code of the Week for Visual Basic.

Your free trial expires after you receive your fourth issue. If you want to continue to receive Code of the Week you can get 52 issues of COTW for only $19.95. This is a full year of Visual Basic source code and information to help with all your development. So don't wait, subscribe now! The quickest way to subscribe is to jump to our online order form at http://www.codeoftheweek.com/order.html