Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

SetTimerF() - SetTimer for Functions


  • Please log in to reply
13 replies to this topic
infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
[ Moderator!: Topic Split from: <!-- m -->http://www.autohotke...pic.php?t=63863<!-- m --> ]

SetTimerF()
SetTimer functionality for functions!
Very many thanks to SKAN who researched and started this, and whos topic this was split from (see link above)*Note: I would not recommended SetTimerF periods less than 30-100ms since weird things could happen if it's called again before the user function is called.
/*
 SetTimerF: 
    An attempt at replicating the entire SetTimer functionality
       for functions. Includes one-time and recurring timers.
    
    Thanks to SKAN for initial code and conceptual research.
    Modified by infogulch to copy SetTimer features
    
 On User Call:
    returns: true if success or false if failure
    p1: Function name
    p2: Delay (int)(0 to stop timer, positive to start, negative to run once)
    p3: (optional) Pointer to data (uInt)(must be persistent at another location)
    p4: (optional) Length of data (uInt)
    Note: p3 & p4 don't necessarily have to be a pointer and length
       , but they must be numerical (positive/negative/float)
    
 On Timer: (user)
    p3 and p4 are passed as the first and second params if the function accepts them
    ErrorLevel is set to the TickCount 
    
 On Timer: (internal)
    p1: HWND (unused)
    p2: uMsg (unused)
    p3: idEvent (timer id) used internally 
       ( as per http://msdn.microsoft.com/en-us/library/ms644907 )
    p4: dwTime (tick count) Set ErrorLevel to this before user function call
*/
SetTimerF( p1, p2="", p3=0, p4=0 ) {
 Static tmrs, CBA
 if !CBA
    CBA := RegisterCallback( A_ThisFunc, "", 4 )
 If IsFunc( p1 ) {
    if RegExMatch(tmrs, "(?i)^(?<pre>.*)(?<=^|;)(?<tmr>\d+)," p1 ",[^;]*;(?<post>.*)$", _)
       ret := DllCall( "KillTimer", UInt,0, UInt, _tmr ), tmrs := _pre _post
    if (p2 = 0)
       return ret
    return !!tmr := DllCall( "SetTimer", UInt,0, UInt,0, UInt,p2 ? Abs(p2) : (p2 := 250), UInt,CBA )
       , tmrs .= tmr "," p1 "," p2 "," (p3+=0) "," (p4+=0) ";"
 }
 RegExMatch(tmrs, "^(?<pre>.*)(?<=^|;)" p3 ",(?<func>[\da-zA-Z@#$_]+),(?<delay>-?\d+),(?<ptr>\d*),(?<len>\d*);(?<post>.*)$", _)
 if (_delay < 0)
    DllCall( "KillTimer", UInt,0, UInt, p3 ), tmrs := _pre _post
 ErrorLevel := p4, %_func%( _ptr, _len )
}

Note there is an AHK_L version a couple posts below.

All Versions: http://db.tt/A0Woi8R

MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009
Thank you for the function! :)
Looks promising, I'll test it ASAP.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.


infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
*Note: I would not recommended SetTimerF timers (either version) faster than 30-100ms since weird things could happen if it's called again before the user function is called.

AHKL version. Slightly different from the standard ahk version. Uses objects, object address references, A_EventInfo, multiple RegisterCallback's.

I'm thinking this should be faster than the standard ahk version, especially when there are a bunch of simultaneous timers.

Beta script. If someone notices a problem or syntax error, please tell me. (especially related to releasing references, since it can be a memory leak)

#Persistent
   SetTimerF("func",100,Object(1,"Hello",2,"World!"))
return

func(hello,world){
 ToolTip % Hello " " world
}

/*
 SetTimerF:
    An attempt at replicating the entire SetTimer functionality
       for functions. Includes one-time and recurring timers.
   
    Thanks to SKAN for initial code and conceptual research.
    Modified by infogulch and HotKeyIt to copy SetTimer features
   
 On User Call:
    returns: true if success or false if failure
    Function: Function name
    Period: Delay (int)(0 to stop timer, positive to start, negative to run once)
    ParmObject: (optional) Object of params to pass to function
    dwTime: (used internally)
   
 On Timer:
    ParmObject is expanded into params for the called function
       (see 
    ErrorLevel is set to the TickCount
   
 Reference: http://msdn.microsoft.com/en-us/library/ms644907
*/
SetTimerF( Function, Period=0, ParmObject=0, dwTime=0 ) {
 Static tmrs:=Object()
 If IsFunc( Function ) {
    if IsObject(tmrs[Function])
       ret := DllCall( "KillTimer", "UInt",0, "UInt,tmrs[Function,"tmr"])
       , DllCall("GlobalFree", "UInt", tmrs[Function,"CBA"])
       , ObjRelease(tmrs[Function,"REF"])
       , tmrs.remove(Function)
    if (Period = 0 || Period ? "off")
       return ret
    Period := (Period && Period!="On") ? Period : 250
    tmrs[Function]:=Object("func",Function,"OneTime",(Period<0),"params",IsObject(ParmObject)?ParmObject:Object())
    tmrs[Function,"REF"] := &tmrs[Function]
    tmrs[Function,"CBA"] := RegisterCallback(A_ThisFunc,"",4,tmrs[Function,"REF"])
    return !!tmrs[Function,"tmr"] := DllCall("SetTimer", "UInt",0, "UInt",0, "UInt",Abs(Period), "UInt",tmrs[Function,"CBA"])
 }
 tmr := Object(A_EventInfo)
 if IsObject(tmr) {
    if (tmr.OneTime)
       DllCall("KillTimer", "UInt",0, "UInt",tmr.tmr)
       , DllCall("GlobalFree", "UInt",tmr.CBA)
       , ObjRelease(tmr.REF)
       , tmr := tmrs.remove(tmr.func)
    ErrorLevel:=dwTime
    tmr.func(tmr.params*)
 }
}
All Versions: http://db.tt/A0Woi8R

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thanks infogulch, I've made some changes, seems to work fine now, example included ;)
#Persistent

SetTimerF("func",100,Object(1,"Hello",2,"World!"))

Return

func(hello,world){

	ToolTip % Hello " " world

}



/*

 SetTimerF:

    An attempt at replicating the entire SetTimer functionality

       for functions. Includes one-time and recurring timers.

   

    Thanks to SKAN for initial code and conceptual research.

    Modified by infogulch and HotKeyIt to copy SetTimer features

   

 On User Call:

    returns: true if success or false if failure

    Function: Function name

    Period: Delay (int)(0 to stop timer, positive to start, negative to run once)

    ParmObject: (optional) Object of params to pass to function

    dwTime: (used internally)

   

 On Timer: (user)

    ParmObject is expanded into params for the called function

    ErrorLevel is set to the TickCount

   

 On Timer: (internal)

    Function: HWND (unused)

    Period: uMsg (unused)

    ParmObject: idEvent (timer id) used internally

       ( as per http://msdn.microsoft.com/en-us/library/ms644907 )

    dwTime: dwTime (tick count) Set ErrorLevel to this before user's function call

*/

SetTimerF( Function, Period=0, ParmObject=0,dwTime=0) {

 Static tmrs:=Object()

 If IsFunc( Function ) {

    if IsObject(tmrs[Function])

       ret := DllCall( "KillTimer", UInt,0, UInt, tmrs[Function,"tmr"])

       , DllCall("GlobalFree", UInt, tmrs[Function,"CBA"])

       , tmrs._remove(Function)

    if (Period = 0 || Period ? "off")

       return ret

       tmrs[Function]:=Object("func",Function,"delay",Period,"OneTime",(Period<0),"params",IsObject(ParmObject)?ParmObject:Object())

       Period := (Period && Period!="On") ? Abs(Period) : (Period := 250)

       tmrs[Function,"CBA"] := RegisterCallback(A_ThisFunc,"","",&tmrs[Function])

       return !!tmr := DllCall("SetTimer", UInt,0, UInt,0, UInt,Period, UInt,tmrs[Function,"CBA"])

       , tmrs[Function,"tmr"] := tmr

 }

 tmr := Object(A_EventInfo)

 if IsObject(tmr) {

	 tmr.func(tmr.params*)

	 if (tmr.OneTime)

	    DllCall("KillTimer", UInt,0, UInt,tmr.tmr)

	    , DllCall("GlobalFree", UInt,tmr.CBA)

	    , tmrs._Remove(tmr.func)

	 ErrorLevel:=dwTime

 }

}


infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
Thanks, HotkeyIt! I figured I'd need a little help on the ahk_l objects syntax. xD I meant to do an example as well, I'm glad the concept isn't so off the wall that at least someone understands it lol.

Some comments on your modifications:
; hmm i dunno if i like the long param names or not. i usually prefer 1-3 character variable names, but i'll leave it
SetTimerF( [color=red]Function, Period=0, ParmObject=0, dwTime=0[/color] ) { 

 ;Oops, forgot to initialize :P
 Static tmrs[color=red]:=Object()[/color]

 If IsFunc( Function ) {
    if IsObject(tmrs[Function])
       ret := DllCall( "KillTimer", UInt,0, UInt, tmrs[Function,"tmr"])
       , DllCall("GlobalFree", UInt, tmrs[Function,"CBA"])

       ; why are we using .[color=red]_[/color]remove() vs just .remove()? https://ahknet.autohotkey.com/~Lexikos/AutoHotkey_L/docs/objects/Object.htm#Remove
       ; note in the link above: "Returns -- The value which was removed, if any"
       , tmrs._remove(Function) 

    if (Period = 0 || Period ? "off")
       return ret

    ; i guess we don't really need to store the period value in our object since it's not used from here
    tmrs[Function]:=Object("func",Function,[color=red]"delay",Period,[/color]"OneTime",([color=orange]Period[/color]<0),"params",IsObject(ParmObject)?ParmObject:Object())
    
    ; this line doesn't do what it needs to if it's not executed before the line above
    ; but then we'd have to move the [color=red]Abs()[/color] down to the SetTimer call
    [color=orange]Period :=[/color] (Period && Period!="On") ? [color=red]Abs([/color]Period[color=red])[/color] : (Period := 250) 

    ; there is a very specific reason that i saved the [color=red]return value[/color]: https://ahknet.autohotkey.com/~Lexikos/AutoHotkey_L/docs/Objects.htm#Refs
    ; correct me if i'm wrong, but using this requires us to manually decrement the reference count
    ; if we don't specify a [color=orange]paramcount[/color], then it won't pass the dwTime param
    tmrs[Function,"CBA"] := RegisterCallback(A_ThisFunc,"",[color=orange]""[/color],[color=red]&tmrs[Function][/color])

    return !!tmr := DllCall("SetTimer", UInt,0, UInt,0, UInt, Period, UInt,tmrs[Function,"CBA"])
       , tmrs[Function,"tmr"] := tmr
 }
 tmr := Object(A_EventInfo)
 if IsObject(tmr) {

    ; thanks for the syntax help
    ; i intentionally and explicitly put the user function call *after* the (possible) KillTimer call
    ; if the period is, for example [color=red]-[/color]100 and tmr.func takes 200ms to complete, it would be called at least twice, which could be bad
    ; since its completion time is completely unknown, we need to call it as late as possible
    [color=red]tmr.func(tmr.params*)[/color]

    if (tmr.OneTime)
       DllCall("KillTimer", UInt,0, UInt,tmr.tmr)
       , DllCall("GlobalFree", UInt,tmr.CBA)
       , tmrs._Remove(tmr.func)

    ; this doesn't really do much unless it comes before the tmr.func() call ...
    [color=red]ErrorLevel:=dwTime[/color]
 }
}

I updated my post above with HotKeyIt's modifications and my new ones, and also linked where you can download old versions.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
I used long names so it is easier to understand the code.
I did not notice that we can use Remove same as _Remove now, thank you.
We need Period as well as Priority ;)

I have enabled Priority now.
Also Timer is suspended when Function is running.
A function with higher priority can interrupt currently running function, while timer with lower priority will be relaunched as soon as possible.
#Persistent
CoordMode,ToolTip,Screen
SetTimerF("func",2000,Object(1,1),10) ;create a higher priority timer
SetTimerF("func2",1000,Object(1,2)) ;another timer with low priority
Return
func(p){
   MsgBox % "Timer number: " p
}
func2(p){
   MsgBox % "Timer number: " p
}

Escape::ExitApp

SetTimerF( Function, Period=0, ParmObject=0, Priority=0 ) { 
 Static current,tmrs:=Object() ;current will hold timer that is currently running
 If IsFunc( Function ) {
    if IsObject(tmr:=tmrs[Function]) ;destroy timer before creating a new one
       ret := DllCall( "KillTimer", UInt,0, UInt, tmr.tmr)
       , DllCall("GlobalFree", UInt, tmr.CBA)
       , tmrs.Remove(Function) 
    if (Period = 0 || Period ? "off")
       return ret ;Return as we want to turn off timer
	 ; create object that will hold information for timer, it will be passed trough A_EventInfo when Timer is launched
    tmr:=tmrs[Function]:=Object("func",Function,"Period",Period="on" ? 250 : Period,"Priority",Priority
								,"OneTime",(Period<0),"params",IsObject(ParmObject)?ParmObject:Object()
								,"Tick",A_TickCount)
    tmr.CBA := RegisterCallback(A_ThisFunc,"F",4,&tmr)
    return !!(tmr.tmr  := DllCall("SetTimer", UInt,0, UInt,0, UInt
								, (Period && Period!="On") ? Abs(Period) : (Period := 250)
								, UInt,tmr.CBA)) ;Create Timer and return true if a timer was created
				, tmr.Tick:=A_TickCount
 }
 tmr := Object(A_EventInfo) ;A_Event holds object which contains timer information
 if IsObject(tmr) {
	 DllCall("KillTimer", UInt,0, UInt,tmr.tmr) ;deactivate timer so it does not run again while we are processing the function
	 If (!tmr.active && tmr.Priority<(current.priority ? current.priority : 0)) ;Timer with higher priority is already current so return
		 Return (tmr.tmr:=DllCall("SetTimer", UInt,0, UInt,0, UInt, 100, UInt,tmr.CBA)) ;call timer again asap
	 current:=tmr
	 tmr.tick:=ErrorLevel :=Priority ;update tick to launch function on time
	 tmr.func(tmr.params*) ;call function
	 current= ;reset timer
    if (tmr.OneTime) ;One time timer, deactivate and delete it
       return DllCall("GlobalFree", UInt,tmr.CBA)
				 ,tmrs.Remove(tmr.func)
	 tmr.tmr:= DllCall("SetTimer", UInt,0, UInt,0, UInt ;reset timer
				,((A_TickCount-tmr.Tick) > tmr.Period) ? 0 : (tmr.Period-(A_TickCount-tmr.Tick)), UInt,tmr.CBA)
 }
}


infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
HotkeyIt:

The script is uninterruptible as a result of Critical or Thread Interrupt/Priority. During such times, timers will not run. Later, when the script becomes interruptible again, any overdue timer will run once as soon as possible and then resume its normal schedule.

I assume this is the same for timers interrupting each other with priorities. If so, when you kill the timer and reinstate it every time not only are you seriously degrading the accuracy of the timer, you're also giving confusing logic to the user.

The timer itself runs with no regard to the return of the call itself. That is, your latest implementation breaks worse the longer the user function takes. For example, if there's a 3 second timer and the user function takes 2 seconds to complete, there should only be 1 second of pause before it's called again. The way you have implemented it there will be 3 additional seconds before the function is called again

And you still aren't using ObjRelease() on the &reference after removing everything, so in theory it never gets released from memory and would considered be a memory leak.

Lexikos, it would be nice to have your input on these things. That is, what happens when a timer of lower priority attempts to interrupt another timer of higher priority and if ObjRelease() is necessary in our codes.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Try following.
#Persistent
CoordMode,ToolTip,Screen
SetTimerF("func",2000,Object(1,1)) ;create a higher priority timer
Return

func(p){
	static now
   now.=A_Min ":" A_Sec ":" A_MSec "`n"
	ToolTip % "Timer`n" now
	[color=red]Sleep, 1000[/color]
}
As you will see the timer runs fine. When timer is overdue (e.g. change the Sleep to 3000, it will run as soon as function finished.

About releasing objects, that should not be a problem. Since we relate the timer with a function.

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Nice function!

Here's an enhanced one with support for Delegates:
SetTimerF( Function, Period=0, ParmObject=0, Priority=0 ) { 
 Static current,tmrs:=Object() ;current will hold timer that is currently running
 If IsFunc( Function ) || IsObject( Function ){
    if IsObject(tmr:=tmrs[Function]) ;destroy timer before creating a new one
       ret := DllCall( "KillTimer", UInt,0, UInt, tmr.tmr)
       , DllCall("GlobalFree", UInt, tmr.CBA)
       , tmrs.Remove(Function) 
    if (Period = 0 || Period ? "off")
       return ret ;Return as we want to turn off timer
    ; create object that will hold information for timer, it will be passed trough A_EventInfo when Timer is launched
    tmr:=tmrs[Function]:=Object("func",Function,"Period",Period="on" ? 250 : Period,"Priority",Priority
                        ,"OneTime",(Period<0),"params",IsObject(ParmObject)?ParmObject:Object()
                        ,"Tick",A_TickCount)
    tmr.CBA := RegisterCallback(A_ThisFunc,"F",4,&tmr)
    return !!(tmr.tmr  := DllCall("SetTimer", UInt,0, UInt,0, UInt
                        , (Period && Period!="On") ? Abs(Period) : (Period := 250)
                        , UInt,tmr.CBA)) ;Create Timer and return true if a timer was created
            , tmr.Tick:=A_TickCount
 }
 tmr := Object(A_EventInfo) ;A_Event holds object which contains timer information
 if IsObject(tmr) {
    DllCall("KillTimer", UInt,0, UInt,tmr.tmr) ;deactivate timer so it does not run again while we are processing the function
    If (!tmr.active && tmr.Priority<(current.priority ? current.priority : 0)) ;Timer with higher priority is already current so return
       Return (tmr.tmr:=DllCall("SetTimer", UInt,0, UInt,0, UInt, 100, UInt,tmr.CBA)) ;call timer again asap
    current:=tmr
    tmr.tick:=ErrorLevel :=Priority ;update tick to launch function on time
    func := tmr.func.(tmr.params*) ;call function
    current= ;reset timer
    if (tmr.OneTime) ;One time timer, deactivate and delete it
       return DllCall("GlobalFree", UInt,tmr.CBA)
             ,tmrs.Remove(tmr.func)
    tmr.tmr:= DllCall("SetTimer", UInt,0, UInt,0, UInt ;reset timer
            ,((A_TickCount-tmr.Tick) > tmr.Period) ? 0 : (tmr.Period-(A_TickCount-tmr.Tick)), UInt,tmr.CBA)
 }
}


_3D_
  • Members
  • 79 posts
  • Last active: Mar 14 2014 07:49 PM
  • Joined: 28 Feb 2013

An alternative to have timed function: http://www.autohotke...me/#entry612957

and code:

;timedFunction sintax ###############################################
tf_timedFunction(a1, a2, timer=0, timed=1)
;a1    - argument 1
;a2    - argument 2
;timer - period in ms to run function body
;timed - 0=run now and timed / 1=run only timed
{ ;===== variable definitions only ==================================
  static arg1, arg2 ;must be STATIC or GLOBAL
  ;===== this section will run only ONCE when first function call === 
    
  ;===== variable initializing ======================================
  arg1:= a1, arg2:= a2 
  ;===== this section will run ANY function call ====================
    
  ;===== timer initialization =======================================
  if timer = 0 ;run as normal function
    SetTimer, LABEL_tf_timedFunction, Off
  else         ;run now or timed
  { SetTimer, LABEL_tf_timedFunction, %timer%
    if timed   ;TRUE=timed / FALSE=run now
      return   
  }
  ;==================================================================
  
  ;===== timedFunction BODY =========================================
  LABEL_tf_timedFunction:   ;unique LABEL
    ;----- function actions -----------------------------------------
    ToolTip,% arg1++ ":" arg2--
    ;----- any function action clone MUST end with return -----------
  RETURN ;end of function return MUST present
  ;==================================================================
} ; #################################################################

USAGE:
tf_timedFunction(1, 1,  1000, 0) ;run now and 1000 ms timed
tf_timedFunction(1, 1,  1000)    ;run 1000 ms timed
tf_timedFunction(1, 1, -1000)    ;run once after 1000 ms
tf_timedFunction(1, 1)           ;run as normal function 1 time only


_3D_
  • Members
  • 79 posts
  • Last active: Mar 14 2014 07:49 PM
  • Joined: 28 Feb 2013

timedFunction - improved release 

;timedFunction sintax ###############################################
tf_timedFunction(_a1, _a2, _timer=0, _timed=1)
;_a1    - argument 1
;_a2    - argument 2
;timer - period in ms to run function body
;timed - 0=run now and timed / 1=run only timed
{ ;===== variable definitions only ==================================
  static a1, a2, timer ;must be STATIC or GLOBAL
  ;===== this section will run only ONCE when first function call === 
    
  ;===== variable initializing ======================================
  a1:= _a1, a2:= _a2 
  ;===== this section will run at ANY native function call ==========
    
  ;===== timer initialization =======================================
  if (timer:=_timer) ;init static timer
  { SetTimer, LABEL_tf_timedFunction, %timer%
    if _timed ;TRUE= only timed / FALSE= now and timed
      return   
  }
  ;else SetTimer, LABEL_tf_timedFunction, Off ;run only once
  ;see below
  ;===== this section will run at ANY native function call ==========
  
  ;===== timedFunction BODY =========================================
  LABEL_tf_timedFunction:   ;unique LABEL
    SetTimer, LABEL_tf_timedFunction, Off
    
    ;----- function actions -----------------------------------------
    ToolTip,% arg1++ ":" arg2--
    ;----- any function action clone MUST end with return -----------
    
    ;0 - no timing
    ;- - run only once timed
    ;+ - set timer back
    if timer > 0
    SetTimer, LABEL_tf_timedFunction  
  RETURN ;end of function return MUST present
  ;==================================================================
} ; #################################################################


Guest10
  • Members
  • 1216 posts
  • Last active: Oct 30 2015 05:12 PM
  • Joined: 27 Oct 2012

could you give an example for this code (support for Delegates)?

Nice function!

Here's an enhanced one with support for Delegates:

SetTimerF( Function, Period=0, ParmObject=0, Priority=0 ) { 
 Static current,tmrs:=Object() ;current will hold timer that is currently running
 If IsFunc( Function ) || IsObject( Function ){
    if IsObject(tmr:=tmrs[Function]) ;destroy timer before creating a new one
       ret := DllCall( "KillTimer", UInt,0, UInt, tmr.tmr)
       , DllCall("GlobalFree", UInt, tmr.CBA)
       , tmrs.Remove(Function) 
    if (Period = 0 || Period ? "off")
       return ret ;Return as we want to turn off timer
    ; create object that will hold information for timer, it will be passed trough A_EventInfo when Timer is launched
    tmr:=tmrs[Function]:=Object("func",Function,"Period",Period="on" ? 250 : Period,"Priority",Priority
                        ,"OneTime",(Period<0),"params",IsObject(ParmObject)?ParmObject:Object()
                        ,"Tick",A_TickCount)
    tmr.CBA := RegisterCallback(A_ThisFunc,"F",4,&tmr)
    return !!(tmr.tmr  := DllCall("SetTimer", UInt,0, UInt,0, UInt
                        , (Period && Period!="On") ? Abs(Period) : (Period := 250)
                        , UInt,tmr.CBA)) ;Create Timer and return true if a timer was created
            , tmr.Tick:=A_TickCount
 }
 tmr := Object(A_EventInfo) ;A_Event holds object which contains timer information
 if IsObject(tmr) {
    DllCall("KillTimer", UInt,0, UInt,tmr.tmr) ;deactivate timer so it does not run again while we are processing the function
    If (!tmr.active && tmr.Priority<(current.priority ? current.priority : 0)) ;Timer with higher priority is already current so return
       Return (tmr.tmr:=DllCall("SetTimer", UInt,0, UInt,0, UInt, 100, UInt,tmr.CBA)) ;call timer again asap
    current:=tmr
    tmr.tick:=ErrorLevel :=Priority ;update tick to launch function on time
    func := tmr.func.(tmr.params*) ;call function
    current= ;reset timer
    if (tmr.OneTime) ;One time timer, deactivate and delete it
       return DllCall("GlobalFree", UInt,tmr.CBA)
             ,tmrs.Remove(tmr.func)
    tmr.tmr:= DllCall("SetTimer", UInt,0, UInt,0, UInt ;reset timer
            ,((A_TickCount-tmr.Tick) > tmr.Period) ? 0 : (tmr.Period-(A_TickCount-tmr.Tick)), UInt,tmr.CBA)
 }
}


Guest10
  • Members
  • 1216 posts
  • Last active: Oct 30 2015 05:12 PM
  • Joined: 27 Oct 2012

Any examples that run this code?

 

timedFunction - improved release 

;timedFunction sintax ###############################################
tf_timedFunction(_a1, _a2, _timer=0, _timed=1)
;_a1    - argument 1
;_a2    - argument 2
;timer - period in ms to run function body
;timed - 0=run now and timed / 1=run only timed
{ ;===== variable definitions only ==================================
  static a1, a2, timer ;must be STATIC or GLOBAL
  ;===== this section will run only ONCE when first function call === 
    
  ;===== variable initializing ======================================
  a1:= _a1, a2:= _a2 
  ;===== this section will run at ANY native function call ==========
    
  ;===== timer initialization =======================================
  if (timer:=_timer) ;init static timer
  { SetTimer, LABEL_tf_timedFunction, %timer%
    if _timed ;TRUE= only timed / FALSE= now and timed
      return   
  }
  ;else SetTimer, LABEL_tf_timedFunction, Off ;run only once
  ;see below
  ;===== this section will run at ANY native function call ==========
  
  ;===== timedFunction BODY =========================================
  LABEL_tf_timedFunction:   ;unique LABEL
    SetTimer, LABEL_tf_timedFunction, Off
    
    ;----- function actions -----------------------------------------
    ToolTip,% arg1++ ":" arg2--
    ;----- any function action clone MUST end with return -----------
    
    ;0 - no timing
    ;- - run only once timed
    ;+ - set timer back
    if timer > 0
    SetTimer, LABEL_tf_timedFunction  
  RETURN ;end of function return MUST present
  ;==================================================================
} ; #################################################################


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

Updated for 64-bit and some fixes for priority:

#Persistent
CoordMode,ToolTip,Screen
SetTimerF("func",2000,Object(1,1),10) ;create a higher priority timer
SetTimerF("func2",10,Object(1,2)) ;another timer with low priority
Return
func(p){
   MsgBox % "Timer number: " p
}
func2(p){
   ToolTip % "Timer number: " p
}

Escape::ExitApp

SetTimerF( Function, Period=0, ParmObject=0, Priority=0 ) { 
 Static current,tmrs:=[] ;current will hold timer that is currently running
 If IsFunc( Function ) {
    if IsObject(tmr:=tmrs[Function]) ;destroy timer before creating a new one
       ret := DllCall( "KillTimer", UInt,0, PTR, tmr.tmr)
       , DllCall("GlobalFree", PTR, tmr.CBA)
       , tmrs.Remove(Function) 
    if (Period = 0 || Period = "off")
       return ret ;Return as we want to turn off timer
	 ; create object that will hold information for timer, it will be passed trough A_EventInfo when Timer is launched
    tmr:=tmrs[Function]:={func:Function,Period:Period="on" ? 250 : Period,Priority:Priority
								,OneTime:Period<0,params:IsObject(ParmObject)?ParmObject:Object()
								,Tick:A_TickCount}
    tmr.CBA := RegisterCallback(A_ThisFunc,"F",4,&tmr)
    return !!(tmr.tmr  := DllCall("SetTimer", PTR,0, PTR,0, UInt
								, (Period && Period!="On") ? Abs(Period) : (Period := 250)
								, PTR,tmr.CBA,"PTR")) ;Create Timer and return true if a timer was created
				, tmr.Tick:=A_TickCount
 }
 tmr := Object(A_EventInfo) ;A_Event holds object which contains timer information
 if IsObject(tmr) {
	 DllCall("KillTimer", PTR,0, PTR,tmr.tmr) ;deactivate timer so it does not run again while we are processing the function
	 If (current && tmr.Priority<current.priority) ;Timer with higher priority is already current so return
		 Return (tmr.tmr:=DllCall("SetTimer", PTR,0, PTR,0, UInt, 100, PTR,tmr.CBA,"PTR")) ;call timer again asap
	 current:=tmr
	 ,tmr.tick:=ErrorLevel :=Priority ;update tick to launch function on time
	 ,tmr.func(tmr.params*) ;call function
    if (tmr.OneTime) ;One time timer, deactivate and delete it
       return DllCall("GlobalFree", PTR,tmr.CBA)
				 ,tmrs.Remove(tmr.func)
	 tmr.tmr:= DllCall("SetTimer", PTR,0, PTR,0, UInt ;reset timer
				,((A_TickCount-tmr.Tick) > tmr.Period) ? 0 : (tmr.Period-(A_TickCount-tmr.Tick)), PTR,tmr.CBA,"PTR")
	 current= ;reset timer
 }
}