Timer Control

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

Previous topic - Next topic

Douglas McDonald

I must really be missing something. I have a timer control on the form. The very first thing I do is call StopTimer
Sub StopTimer(hWndForm As Dword)

KillTimer hWndForm, IDC_FRMDMM_TIMER1
MsgBox Str$(hWndForm) & "  " & "killtimer"
End Sub


Problem is it never kills the timer:
This event keep firing every 10 seconds like its setup in the properities:

Function FRMDMM_TIMER1_WM_TIMER ( _
                                hWndForm      As Dword, _  ' handle of Form
                                wTimerID      As Dword  _  ' the timer identifier
                                ) As Long
MsgBox "timer"
'Writedata
End Function


I was hoping to stop the timer on start up:
Function FRMDMM_WM_CREATE (hWndForm As Dword, _      ' handle of Form
                          ByVal UserData As Long _  ' optional user defined Long value
                          ) As Long

Local ret As Long
StopTimer hWndForm
ret = SetListHeader(hWndForm)
End Function


Then kill the timer:

Sub StopTimer(hWndForm As Dword)

KillTimer hWndForm, IDC_FRMDMM_TIMER1
MsgBox Str$(hWndForm) & "  " & "killtimer"
End Sub


Then Start the timer when I need it:

Sub Set_Timer_Int(hWndForm As Dword,tm As Long )
' 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)
KillTimer hWndForm, IDC_FRMDMM_TIMER1 'kill first
HWND_FRMDMM_TIMER1 = SetTimer( HWND_FRMDMM_TIMER1, IDC_FRMDMM_TIMER1, tm, ByVal %Null )       


End Sub


BTW I tried both  HWND_FRMDMM_TIMER1 and hWndForm. I'm very new to FireFly so I haven't figured out what the difference is in the two if any other than ones passed and the other is global

It looks to me that killtimer just doesn't work but it seems to work for others here if outside the timer event like In using it. What am I doing wrong

Also why isn't there any HELP for timers or any FF commands like for the other controls? Its no big deal but it should be consistent with the rest of the controls.

Thanks
Doug

One full day using FF3 and so far this is the only major issue
Doug McDonald
KD5NWK
www.redforksoftware.com
Is that 1's and 0's or 0's and 1's?

Paul Squires

Hi Doug,

In your case you will not be able to kill the timer in the WM_CREATE message handler because at that point the timer does not yet exist. Here is what the Firefly generated code looks like:


         ' The form is being created, but not yet shown. Allow the user to process their
         ' commands prior to showing the Form's controls.
         FLY_nResult = FORM1_WM_CREATE (hWndForm, @FLY_UserData)
             If FLY_nResult Then Function = FLY_nResult: Exit Function
         
         ' Create any user defined Timer controls
         SetTimer hWndForm, IDC_FORM1_TIMER1, 2000, ByVal %Null


As you can see, the call to create the timer occurs after the call to the function to handle the WM_CREATE message from the user. The question will then be, why not have the timer created before the WM_CREATE message handler? I could do that but then it creates another problem for those people who want to initialize their controls in WM_CREATE prior to any timer firing. Chicken and egg sort of problem.

A simple solution would be to create a global variable, say, GLOBAL fStopTimer As Long and set it to %TRUE in your WM_CREATE handler. Then in the WM_TIMER handler, test that variable and exit as approproate.

For example,


Global fStopTimer As Long

'--------------------------------------------------------------------------------
Function FORM1_WM_CREATE ( _
                         hWndForm As Dword, _      ' handle of Form
                         ByVal UserData As Long _  ' optional user defined Long value
                         ) As Long
   
   ' Stop the timer from firing. To re-enable the timer, set this
   ' variable to %FALSE.
   fStopTimer = %TRUE
   
End Function


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

   If fStopTimer = %TRUE Then Exit Function
   
   Static nCount As Long
   Incr nCount
   FF_Control_SetText hWndForm, "Timer has fired again for the" & Str$(nCount) & " time."

End Function


'--------------------------------------------------------------------------------
Function FORM1_COMMAND1_BN_CLICKED ( _
                                   ControlIndex     As Long,  _  ' index in Control Array
                                   hWndForm         As Dword, _  ' handle of Form
                                   hWndControl      As Dword, _  ' handle of Control
                                   idButtonControl  As Long   _  ' identifier of button
                                   ) As Long

   ' Toggle starting and stopping the Timer
   fStopTimer = Not fStopTimer
   
End Function

Paul Squires
PlanetSquires Software

Martin Francom

Paul,
   With this method the Timer Control continues to fire every interval.  If the interval is very short 100msec say will this cause any negative performance issues?   
   I would prefer to turn the Timer Control off so it stops.    Would it be possible to use post message to the message handler from the WM_TIMER function  that would then KillTimer?   

Paul Squires

Quote from: Marty Francom on January 12, 2010, 02:04:37 PM
Paul,
   With this method the Timer Control continues to fire every interval.  If the interval is very short 100msec say will this cause any negative performance issues?   
   I would prefer to turn the Timer Control off so it stops.    Would it be possible to use post message to the message handler from the WM_TIMER function  that would then KillTimer?   

I have read this thread a couple of times and I still can not figure what the problem is that you are having. There is no problem killing a timer from within WM_TIMER message. David Kenny offered very good advice in a previous post in this thread - kill the timer before running your time intensive task.

Other than that, you may want to test to ensure that the WM_TIMER is not being re-entered before the previous WM_TIMER has completed. Not sure if WM_TIMER is synchronous - ie. it waits until fully complete before processing the next WM_TIMER. Maybe they are all queued in the message loop - not sure. Nonetheless, if you have a very short time interval (say, 100 milliseconds) then you can do the following to ensure that it is no re-entrant.


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

   Static fProcessing As Long
   
   ' If we are already processing a timer message then
   ' don't allow us to start to process another.
   If fProcessing Then Exit Function
   
   ' Set the flag to prevent the timer message being re-entrant
   fProcessing = %TRUE
   
   
   ' Do you thing that you want to do.....
   
   
   ' Allow us to deal with the next timer message
   fProcessing = %FALSE
   
End Function


Quote
If the interval is very short 100msec say will this cause any negative performance issues?   
I doubt it.

Paul Squires
PlanetSquires Software

John Montenigro

Paul,
I have a small app that I wanted to "time out" if not registered. Originally, I put a Timer control on the main form, and in WM_CREATE, I checked the registration. If OK, then I'd KILLTIMER. But I found that the timer would never die. So instead, I removed the Timer control from the form and then if the registration failed, I called SetTimer to create the desired timed exit... a reasonable workaround, I think.

But then I found out that FF3 had a feature that I wasn't using...

Whereas with FF2 I placed all my initialization code in the main form's WM_CREATE, with FF3, it appears that I should place all my initialization code in FF_AppStart.

Is that correct? And would that avoid the kill timer problem?

-John

David Kenny

John,

Can you reproduce that? Or do you have the old code you could post?  I have never had a problem killing a timer in FF.

David

Paul Squires

Quote from: John Montenigro on January 12, 2010, 04:07:46 PM
I put a Timer control on the main form, and in WM_CREATE, I checked the registration. If OK, then I'd KILLTIMER. But I found that the timer would never die. 
Refer to my reply above to Douglas McDonald. You can not kill a timer in the WM_CREATE because it does not exist at that point. Likewise, it does not exist in FF_AppStart.
Paul Squires
PlanetSquires Software

Roger Garstang

Petzold in Programming Windows says:

"You can allocate a timer for your Windows program by calling the SetTimer function. SetTimer includes an unsigned integer argument specifying a time-out interval that can range (in theory) from 1 msec (millisecond) to 4,294,967,295 msec, which is nearly 50 days. The value indicates the rate at which Windows sends your program WM_TIMER messages. For instance, an interval of 1000 msec causes Windows to send your program a WM_TIMER message every second.

When your program is done using the timer, it calls the KillTimer function to stop the timer messages. You can program a "one-shot" timer by calling KillTimer during the processing of the WM_TIMER message. The KillTimer call purges the message queue of any pending WM_TIMER messages. Your program will never receive a stray WM_TIMER message following a KillTimer call."

Many people have said he was wrong though.  Sometime back I myself received a stray WM_TIMER after a KillTimer call, and MSDN says- "The KillTimer function does not remove WM_TIMER messages already posted to the message queue."  Not sure about the sync of the messages.  Multiple WM_TIMER messages are supposed to be consolidated into one, but you can get them quick if one is delayed by your app being busy right up to the point the next one fires you can get two in a row really quick.  I guess one quick test would be a delay/sleep/wait function in WM_TIMER with a global/static to save if entered pause the thread/function at least twice the interval then reset the global and see if another is sent while the global is set. Same could be done to test KillTimer then if determined that only one at a time is sent- Pause function and kill the timer then see if another gets sent that was in the queue.

Roger Garstang

#23
Hmm, just made a form with only a 1sec(1000 interval) timer and:


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

If test = 1 Then MsgBox "Another WM_TIMER": Exit Function
Incr test
For temp = 1 To 2147483647:Next temp
FF_Control_SetText(HWND_FORM1, "1")
For temp = 1 To 2147483647:Next temp
FF_Control_SetText(HWND_FORM1, "2")
For temp = 1 To 2147483647:Next temp
FF_Control_SetText(HWND_FORM1, "3")
For temp = 1 To 2147483647:Next temp
FF_Control_SetText(HWND_FORM1, "4")
For temp = 1 To 2147483647:Next temp
FF_Control_SetText(HWND_FORM1, "5")

KillTimer(HWND_FORM1, IDC_FORM1_TIMER1)
End Function


Tried sleep too with both giving about the same results.  Never got a stray timer pulse after KillTimer.  Change KillTimer line to test= 2 and you can test for multiple Timer messages at the same time (Although since it only tests the first time anyway the KillTimer test does both).  Faster machines may need more For loops or larger numbers to loop through.  Toss in a FFDoEvents though and you never get back to the original function or do but with difficulty...I ran some tests with message boxes that were messy.  First test I ran was just one sleep for 20000 (20sec) and once it stopped sleeping I got like 20 message boxes at once then the app closed which to me would mean they are queued up and not sent as one WM_TIMER like MSDN states, but I ran it again and didn't get the same result...so??? Could vary by OS too.  Probably best to be on safe side though and maybe have a 3 state global where 0 is not in WM_TIMER, 1 is in WM_TIMER and 2 is timer killed (Or just make both 1 and have a 2 state) and if not 0 exit WM_TIMER function/message.  That way you cover all bases.

Martin Francom

Paul,
    I may be all "bassackwards" in my mind about this timer issue or maybe  I am not explaning the problem correctly.
   Attached here is a very simple example program that I think demonstartes what I am trying to say.
   When you run the program, the timer will initially be stopped.  Click the "Start Timer" button you will note that the Timer is firing (see count in Label )  Click "Interval 0" button and timer counts faster (as expected??  actually I would have thought that a zero setting would have disable the timer...my bad)   Now, click the button "Stop Timer"  and indeed the timer stops.
    Now note that in the  WM_Timer function is the statement   "KillTimer ....",   one would think that that statement would stop the timer but it doesn't.   The KillTimer statement appears to only work if executed outside of the WM_TIMER  function.
....Marty   

David Kenny

#25
I just threw together an example project to test Timer's in FF.  I hope someone finds it useful.

It has two FF "Timer Controls".  Timer1 has a five second interval and Timer2 has a one second interval. Both start as soon as they are created (I believe Paul stated earlier that they are created at the same time as the other controls on the form).  I don't bother to kill these timers(see changes below to test Marty's last issue) or change their intervals.  You could kill one the first time it fires by inserting a call to KillTimer in the first line of the it's wm_timer routine. If you do change their interval, remember to save the new handle to the timer in the variable FF created for that control.

It has another timer created on the fly.  I let the user start and stop this timer as they wish.  You could easily do that with code also. (Douglas, this is how you could have handled your situation).

Marty got his post in as I was working on this post.  I modified Timer1 to stop after two iterations.

I kept it as simple as possible to make it easy to follow. 

David

Paul Squires

Quote from: Marty Francom on January 12, 2010, 07:13:21 PM
Paul,
    I may be all "bassackwards" in my mind about this timer issue or maybe  I am not explaning the problem correctly.
   Attached here is a very simple example program that I think demonstartes what I am trying to say.
   When you run the program, the timer will initially be stopped.  Click the "Start Timer" button you will note that the Timer is firing (see count in Label )  Click "Interval 0" button and timer counts faster (as expected??  actually I would have thought that a zero setting would have disable the timer...my bad)   Now, click the button "Stop Timer"  and indeed the timer stops.
    Now note that in the  WM_Timer function is the statement   "KillTimer ....",   one would think that that statement would stop the timer but it doesn't.   The KillTimer statement appears to only work if executed outside of the WM_TIMER  function.
....Marty   

Hi Marty,

I am getting completely different results with the EXE in your download versus the EXE that I create on my computer using your source. Your EXE continually increments the counter whereas mine stops after 5. See the attached archive containing my exe and Marty's. Do they run the same on your computer? (I am running Win 7).

Very strange. Marty, I assume that you compiled on WinXP, right?

(David, I haven't downloaded your project yet).
Paul Squires
PlanetSquires Software

Rolf Brandt

Hi Marty,
when I ran the compiled program you sent it worke exactly as you described. After I compiled it here under XP it behaves properly. Setting the interval to zero disables the timer.

Win7 specific????
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)

David Kenny

#28
Marty,

I get the same results as Paul. The executable you provided runs as you described.  The one that I get when I compile your code works like you want it to.

Rolph,

I haven't found that setting the interval to zero disables the timer. Check out the example I posted above.
In the FORM1_COMMAND2_BN_CLICKED routine,
Change the:    Killtimer %Null, hMyTimer
To:                hMyTimer=Settimer (%Null, %Null, 0, Codeptr(MyTimerProc))

Run the program again and start and stop the timer.

David
Windows XP SP2

Paul Squires

Setting the timer interval should not disable the timer. According to http://msdn.microsoft.com/en-us/library/ms644906(VS.85).aspx using a value < USER_TIMER_MINIMUM then the value is set to USER_TIMER_MINIMUM (which I believe is 10 milliseconds - defined winuser.h).

Marty - do you have gremlins in your machine or something?  :)
Paul Squires
PlanetSquires Software