PDA

Click to See Complete Forum and Search --> : VB6 SelfTimer class module [2008-06-15]


Merri
Jun 14th, 2008, 11:44 PM
Ever wanted to have a better Timer as a class, but have been bothered by the fact that you still need to have two files?

SetTimer API requires that you give an address of a callback procedure that the timer then triggers. Working around the issue has been a problem because AddressOf only works for procedures in a regular module.

This shouldn't worry you anymore. This is a single file solution that has it's roots on the famous SelfSub/SelfHook/SelfCallback solution by Paul Caton and LaVolpe. In this class however the code has been modified and simplified to fit the use of a simple timer.


Events

Timer(Seconds)
This event triggers just like the regular Timer event. Unlike the regular Timer control, you also get the value that tells how many seconds has passed since the first call. The seconds value is reseted always when Interval property is changed, but you may change the Enabled property without reseting the starting time.


Properties

Enabled
Returns or sets whether the timer is enabled or not. By default this is True. However the timer does not run if Interval is 0 even if it is enabled. Enabled is automatically swithed to False if for some reason the initialization of the timer fails. The timer is initialized every time Enabled = True and Interval > 0.

Interval
Returns or sets the interval of the timer in milliseconds. Can only be 0 or a positive value. By default the value is 0.


Usage

In the general declarations, add the following:

Dim WithEvents Timer As SelfTimer

This way you add the control with the Timer event to the form or object. When you wish to initialize the timer:

Set Timer = New SelfTimer
Timer.Interval = 1000

This creates the timer and makes it trigger Timer event every one second. Also remember to cleanup once you're done:

Set Timer = Nothing


Because of using WithEvents, you also have a procedure where you can place the code you want to run.

Private Sub Timer_Timer(ByVal Seconds As Currency)
' your code here
End Sub


A demonstration is included in the attachment. Enjoy!


IMPORTANT! THERE IS A NEW VERSION IN POST #5 (http://www.vbforums.com/showthread.php?t=527281#post3270846)!

DigiRev
Jun 14th, 2008, 11:59 PM
Perfect. :D

GRPsuper9
Jun 15th, 2008, 12:03 AM
Inspired by me :)

Edgemeal
Jun 26th, 2008, 01:48 PM
You have new version? v1.1 on PSC (http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=70705&lngWId=1) ?

Merri
Jun 30th, 2008, 03:02 PM
Here is version 1.1 of SelfTimer, finally had some extra spare time so I could post the update.

This update simplifies the SelfCallback code as well as makes it much more efficient (thanks to Paul Caton's improved solution in his Universal DLL Caller). I also made code more "self commented" by making the machine code portion a user defined type (instead of it being a plain long array that is accessed with pre-defined constants).

Also, changing interval doesn't reset the second counter anymore, that can be done separately with Reset.

VbCoder2008
Nov 14th, 2008, 04:55 PM
nice thanks.

okosv
Nov 19th, 2008, 11:35 PM
thanks a lot!

edhanz
May 20th, 2009, 12:32 AM
Hi Merri,

thank you for this. i used 7 standard timer controls before, and now i use this class. i don't find any trouble of using it. i can use 7 instances of SelfTimer just like standard timer control..

but i want to know, what is the advantage of using this class instead of standard timer control (beside of long interval, and giving an address of a callback procedure if using SetTimer API) ? my previous app is 200 KB in size, but when using this, it increases to 220 KB in size..

thank you..

edhanz
May 20th, 2009, 03:43 AM
and 1 more thing..
do i have to destroy it in Form_Terminate? currently i destroy it in Form_QueryUnload

Merri
May 20th, 2009, 03:51 PM
I don't see a problem in destroying, as long as it gets properly destroyed (Set Nothing) there should be no issues.

The biggest advantage is the 1 ms accuracy you can get, VB's own Timer can't do as good of a job with that (especially on older Windows machines that maxed out at 35 ms or so). There is also a feature difference: a Timer will not execute when the application is busy, instead it just drops the event as if it never happened. SelfTimer will execute it after application is no longer busy, meaning if there are 5 events to trigger they are all triggered in a row.

SelfTimer can be customized further into a specialized Timer or a class that has a built-in timer. You can't add a Timer into a class (without creating it into an existing form).


If you need smaller executable size for some reason, you should be using an executable compressor. It is impossible to make custom code without it showing up in final executable size.

Nanni
Jan 13th, 2010, 02:57 AM
Hi Merri

I'm trying your class, but I have a problem with Timer settings' interval.

Setting Timer.Interval =1000 ,
I get the timer fires every second (1000ms = 1second).

When I set 40ms, the timer do not fires 25 time in 1 second,
but fires about 50 time in 1 second.

Can you help me understand what I'm doing wrong?

Thanks for your time
Nanni

Merri
Jan 13th, 2010, 04:03 AM
That would be interesting, and I'm probably unable to help with the issue. The only thing that comes to my mind which could cause problems is that you'd be constantly changing the Interval on each Timer event, but that is a very wild guess. Do you have the problem when using minimal code possible to see the amount of triggered events / second?

Also, note that DoEvents may do harm (imo it is actually pretty dangerous to use it with API timer, in case you do use it).

Nanni
Jan 13th, 2010, 03:18 PM
Hi Merri
Thanks for reply.

In my project I used the timer for counting Video Frames.

Now I have build a new project with only your Timer Class and
I don't have the problem.

I think the problem is with my code. :confused:
I will check it better.

Thanks again
Nanni

LaVolpe
Jan 26th, 2010, 12:59 PM
Merri, one comment and one suggestion.

Comment regarding post #10. You said that if 5 events were waiting then when code is no longer busy, all 5 events would be triggered. At most only 1 of those 5 should process. This is because the other WM_Timer events are not queued up. Validation seems to prove this. If you noticed between last event before loop started and when loop ended, about 13 events would have occurred, but only 1 was raised.
Interval was set to 100 ms
SecondsValue: 0.438 when a long loop was started
SecondsValue: 1.703 when long loop exited (queued)
SecondsValue: 1.75 next normal event raised
SecondsValue: 1.86 next normal event raised
SecondsValue: 1.969 etc, etc, etc

Suggestion. Maybe add a StopWatch-like start/stop function that uses QueryPerformanceCounter? This way your class can serve two purposes: timer and high resolution counter.

Merri
Jan 26th, 2010, 03:10 PM
For now I don't have a high interest on VB6 based projects; I may do something but I probably won't. SelfTimer in the other hand is a bit of a project that I'd like to rather keep overly simple than boost it with too many features.

VietPubic
May 1st, 2010, 02:13 PM
Thanks Merri !!
I'm using your code, but now I want declare in a module

Dim WithEvents Timer As SelfTimer

But not work, because i writing a windows service using timer, service not include interface form.

my code

Call SetTimer(0&, 0&, Frequency, AddressOf TimerProc)


But not work, perhap hwnd para not work because not have window

edhanz
Sep 16th, 2010, 06:14 AM
Hi Merri. Do you stil working on this timer? If yes, maybe you can make it so it can be loaded as an array..

Merri
Sep 16th, 2010, 09:22 AM
I prefer to keep the class itself simple. Here is just one way to do achieve arrays; not the only one.

' SelfTimerWrap.cls
Option Explicit

Private m_ID As Byte
Private WithEvents m_Timer As SelfTimer

Public Property Get ID() As Byte
ID = m_ID
End Property

Public Property Let ID(ByVal NewValue As Byte)
m_ID = NewValue
End Property

Public Property Get Interval() As Long
Interval = m_Timer.Interval
End Property

Public Property Let Interval(ByVal NewValue As Long)
m_Timer.Interval = NewValue
End Property

Private Sub Class_Initialize()
Set m_Timer = New SelfTimer
m_Timer.Enabled = True
End Sub

Private Sub Class_Terminate()
Set m_Timer = Nothing
End Sub

Private Sub m_Timer_Timer(ByVal Seconds As Currency)
Select Case ID
Case 1
' your code here
Case 2
' your code here
Case 3
' your code here
End Select
End Sub


Dim Timers(1 To 3) As SelfTimerWrap

' because late bounding wastes CPU for nothing
Set Timers(1) = New SelfTimerWrap
Set Timers(2) = New SelfTimerWrap
Set Timers(3) = New SelfTimerWrap

' we use ID so that we know which code we want to run in the event
Timers(1).ID = 1
Timers(2).ID = 2
Timers(3).ID = 3

' this enables the timers (they're automatically Enabled by the class, just set Interval = 0 to disable)
Timers(1).Interval = 100
Timers(2).Interval = 1000
Timers(3).Interval = 10000