Timer Control

Started by Martin Francom, January 10, 2010, 09:51:33 PM

Previous topic - Next topic

Martin Francom

I must be blind because I can't find any info on the Timer Control in either the
FF-Help nor in the FunctionLibrary.   Could someone show me how to do:

Kill  Timer      What is the Syntax for that command.

What's the command to re-set the Timer Interval?

Paul Squires

Paul Squires
PlanetSquires Software

Martin Francom

#2
Thanks I will add this to my Function Library for when I need to look it up next time


' Stop timer
KillTimer hWndForm, IDC_FORM1_TIMER1

' Start timer. You are essentially creating a new timer' and specifying the new interval.
' 1000 represents the number of milliseconds of delay (1 second = 1000 milliseconds)

HWND_FORM1_TIMER1 = SetTimer( hWndForm, IDC_FORM1_TIMER1, 1000, ByVal %Null )   
   
' Change timer interval. You need to kill the original timer
' and create a new timer with the new interval. In this
' case the new interval is 2 seconds

KillTimer hWndForm, IDC_FORM1_TIMER1
HWND_FORM1_TIMER1 = SetTimer( hWndForm, IDC_FORM1_TIMER1, 2000, ByVal %Null )       
.
.

Martin Francom

I must be using this code wrong.  What I wanted to do is have the Timer fire one time and then stop.  This is the code I tried to use but appearently I am doing something wrong.  I have tried it both with and without "SetTimer".
I know I could use a global and set it after timer fires the first time then test for it and exit the timer function every time it fires after that.  But, I would much rarther turn the timer off.  Any ideas what I am doing wrong?


'--------------------------------------------------------------------------------
Function FORM1_TIMER1_WM_TIMER ( _
                               hWndForm      As Dword, _  ' handle of Form
                               wTimerID      As Dword  _  ' the timer identifier
                               ) As Long

    Initialize

    KillTimer hWndForm, IDC_FORM1_TIMER1
    HWND_FORM1_TIMER1 = SetTimer( hWndForm, IDC_FORM1_TIMER1, 0, ByVal %Null )
   
End Function



I guest what I realy want is a  "RunOnce" function    that is a function that would run one time right after the MainForm is created.

Rolf Brandt

Hi Marty,

what happens? Does it fire again?

I use in the WM_Timer event:
Initialize
KillTimer HWND_FRMABOUT, IDC_FRMABOUT_TIMER1

Like this it executes the Initialize function just once and then 'commits suicide'.

Rolf Brandt
http://www.rbsoft.eu
http://www.taxifreeware.com
I cook with wine, sometimes I even add it to the food.
(W. C. Fields)

Paul Squires

if you only want your timer to fire once then I would use a STATIC variable and test it.


Static fOneTime As Long

If fOneTime = 1 Then Exit Function
fOneTime = 1

Initialize
KillTimer HWND_FRMABOUT, IDC_FRMABOUT_TIMER1


Paul Squires
PlanetSquires Software

Rolf Brandt

Hi Paul,

would the timer still fire after a kill? Or is the static variable just for security in case the timer interval is very short?
Rolf Brandt
http://www.rbsoft.eu
http://www.taxifreeware.com
I cook with wine, sometimes I even add it to the food.
(W. C. Fields)

Roger Garstang

It is possible for timers to fire when killed because their messages are posted to the message queue and there can be stray messages.  That is why Visual Studio wraps it and has an enable method that most likely uses the variable on it firing and if enable=false then it ignores it and doesn't pass the message along.

Rolf Brandt

I thought so. Thanks for the clarification, Roger.
Rolf Brandt
http://www.rbsoft.eu
http://www.taxifreeware.com
I cook with wine, sometimes I even add it to the food.
(W. C. Fields)

Martin Francom

Quote from: Rolf Brandt on January 11, 2010, 08:32:11 AM
Hi Paul,

would the timer still fire after a kill? Or is the static variable just for security in case the timer interval is very short?
Yes, the timer continued to fire.
I will write a little test program to demonstrate what is happening.

It would be nice to have a "Run Once" property on the timer control.

Roger Garstang

#10
Perhaps one of the Wait API functions like WaitForSingleObject would be more of what you are after? They are usually more accurate too since if under load the system may not give you the WM_TIMER message at exactly the time you want...especially if small time frames.  May want to put it in a thread though since it is blocking execution...unless that is what you want.  Could cause undesired graphical and response time issues in GUI if not in a thread and time to wait is long.

Martin Francom

Quote from: Roger Garstang on January 11, 2010, 03:49:46 PM
Perhaps one of the Wait API functions like WaitForSingleObject would be more of what you are after? They are usually more accurate too since if under load the system may not give you the WM_TIMER message at exactly the time you want...especially if small time frames.
Yes, Roger this might be what woulf work.    Can you to show me an example of how this would be called?

Martin Francom

Here's a test project the show the use of the timer control.
It appear from my testing the placing the KillTimer ansd SetTimer calls outside if the  WM_TIMER function they work as expected.

But when either or both KillTimer and SetTimer are inside the WM_TIMER function they
fail to work. Paul's idea to put a global test works to prevent the events from Wm_TIMER from re-running but it does not turn off the timer.   Here's a sample project that you can experiment with.   If you find a way to turn off the timer from within the WM_TIMER function, please post.  Thanks

Roger Garstang

#13
Code works here with it never going above 5, but I have a fast machine, so painting the number in the text box wouldn't cause too much of a delay even at interval 0 for a stray WM_TIMER to sneak in.  Slower machines could have 1+ stray ones though.  As for your sample function, all you'd need to do in the example is put the check first and always kill the timer first thing so no other tasks run that could delay it.  Remember Windows is Multi-Tasking and it may run your app for a few milliseconds then something else too or it will paint the textbox then causes it to draw and in the mean time a WM_TIMER fired before your check and killtimer ran.


Function FORM1_TIMER1_WM_TIMER ( _
                               hWndForm      As Dword, _  ' handle of Form
                               wTimerID      As Dword  _  ' the timer identifier
                               ) As Long

   c = c + 1
   If c > 5 Then
      KillTimer HWND_FORM1, IDC_FORM1_TIMER1
   Else
      FF_TextBox_SetText (HWND_FORM1_LABEL1, Format$(c) ) ' Format$ eliminates the space in numeric values
   End If
End Function


This isn't much different than what Paul mentioned though.  With PB now supporting classes it could be useful for FF to have functionality like Visual Studio with a Timer object since it is making it look like such, but API calls are still needed to manipulate it.  Those with previous versions of PB would be left out though.  I guess until everyone goes to the latest it could be built in code and FF Functions that use some kind of Type Structure for Timers to store their value and enabled state.  Possibly even using the TimerProc version to call an internal FF function that then passes on to the WM_TIMER message if valid.  It would need to use one of the Threadsafe/Interlocked API calls to set the value either way though to eliminate all possibilities of stray WM_TIMERS since at any time another thread could take over the timeslice.

David Kenny

Marty, here is the code you posted up top:
'--------------------------------------------------------------------------------
Function FORM1_TIMER1_WM_TIMER ( _
                               hWndForm      As Dword, _  ' handle of Form
                               wTimerID      As Dword  _  ' the timer identifier
                               ) As Long

    Initialize

    KillTimer hWndForm, IDC_FORM1_TIMER1
    HWND_FORM1_TIMER1 = SetTimer( hWndForm, IDC_FORM1_TIMER1, 0, ByVal %Null )
   
End Function


If you set the interval property for Timer1 then it will wait that many milliseconds before firing your FORM1_TIMER1_WM_TIMER routine. If you don't set it (leaving it at zero), it will fire immediately.

Problem #1  When the FORM1_TIMER1_WM_TIMER routine you showed gets executed the first time, you are killing it (as you should in this case) - and then immediately restarting it with an interval of zero (you should not be doing this).  Calling SetTimer again (just to zero the interval) does set the interval to zero, but also starts the timer again.

Problem #2  Calling SetTimer fires the timer every "interval" milliseconds until you either change the interval with another call to SetTimer, or you kill the timer. In your example you call your initialize routine before you kill the timer.  If your routine takes more than "interval" milliseconds, the timer will fire again.  You should kill the timer first, then call your initialize routine.

Try:Function FORM1_TIMER1_WM_TIMER ( _
                               hWndForm      As Dword, _  ' handle of Form
                               wTimerID      As Dword  _  ' the timer identifier
                               ) As Long

    KillTimer hWndForm, IDC_FORM1_TIMER1     'This will only fire once unless restarted outside this routine.

    Initialize

   
End Function


David