AutoHotkey Community

It is currently May 27th, 2012, 5:04 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: February 3rd, 2008, 2:28 am 
Offline

Joined: October 15th, 2007, 7:23 pm
Posts: 252
Edit: 2/04/2008 Corrected bug where all timers get shut off when first one killed.
Edit: 2/04/2008 Replaced WM_TIMER handler with Windows TimerProc callback function.
Edit: 2/06/2008 Added abiltity to use a callback function as a target.
Edit: 5/02/2010 Fixed bug with single shot timers. Added single shot timer to demo.

Hi, ALL

Having read of a request in "Ask For Help" for creating a timer on the fly, I wrote this function that uses no globals or labels to run Windows timers (more than 1 at a time) using DllCalls to Windows SetTimer function. Instead of calling labels the function sets a caller's BYREF variable to true or calls a user callback function each time the timer fires. Here is a prototype call for the function:
Code:
SetWinTimer(hGui,TimerID,TimeInMiliseconds,TimerFired,true ;or false)

hGui is the handle of Gui window.
TimerID is an integer 0x1000 or larger.
TimeInMiliseconds is the duration for the timer in miliseconds.
TimerFired is:

1)The callers BYREF Global or Static variable that will be set true when the timer fires. This can be a dynamic variable so in that sense this is a dynamic timer function.

OR

2)A string containing the name of a function to call when the timer fires. This function must accept 5 parameters. See comments in the function script for more info. The last parameter in the call to SetWinTimer must be 1 to invoke the target function version of the function.

To turn off a timer call SetWinTimer same as above but with a time of 0. To kill all timers call as above with a window Gui handle (hGui) of 0.

Returns 0 for failure and >0 for success (Timer ID). Calls to kill individual timers return 0 when no timers left to kill and 1 if a timer(s) is still running.

This function does not use OnMessage to temporarily trap WM_TIMER messages so should not interfere with other timers in your script.

I don't know if this will solve the "Ask For Help" poster's problem but thought I would share it here since I messed around with it for a couple of hours (Edit: now even longer).

The following is a script to demonstrate use of the function. Save it in a folder of your choice such as C:\Program Files\AutoHotKey. Later in this post you will see code for the function itself. Save it as SetWinTimer.ahk in the same folder as this demo script and uncomment the line #include SetWinTimer.ahk in the demo script... OR save the function's code in your user library as SetWinTimer.ahk.

Please read the comments in the demo and function scripts for more iinformation.
Code:
;******************************************************
; Demonstration of SetWinTimer Function using a variable
; target and a function target.
;
; Starts 2 repeating timers set to 4 seconds and allows
; them to run 3 times each. Each time a timer fires
; a variable is set to true (1) or a function is called.
;
; SetWinTimer.ahk file should be in the same folder as
; this script or in the AHK user library folder.
;
; 2/4/2008
; Edit: 5/02/2010 Changed demo to demostrate single shot timer.
; dmatch @ AutoHotKey forum
;******************************************************

;#include SetWinTimer.ahk ;comment/uncomment as needed

CoordMode,ToolTip,Screen
Gui,Add,Button,vStartTimers1 gStartTimers1,Demo Timers With Target Variables
Gui,Add,Button,vStartTimers2 gStartTimers2,Demo Timers With Target Function
Gui,Show,w200 h100, Timer Demo
WinActivate, Timer Demo
WinWaitActive, Timer Demo
hGui:=WinExist() ;Get window handle for use in SetWinTimer
NextTimerID:=0x1000 ;Seed for timer values
return

StartTimers1: ;run variable target version
   DemoText=Please Wait....`n
   ToolTip,,,,1
   GuiControl,Disable,StartTimers1
   GuiControl,Disable,StartTimers2
   Timer1Fired:=0 ;These are boolean indicators that timers have fired
   Timer2Fired:=0
   TimeInMiliseconds:=3000 ;Duration in miliseconds (3 seconds)
   Loop,2
   {
       NextTimerID++
       TimerID%A_Index%:=NextTimerID
       if(A_Index=2)
             TimeInMiliseconds:=-1*TimeInMiliseconds ;demo single shot timer
       result:=SetWinTimer(hGui,TimerID%A_Index%,TimeInMiliseconds,Timer%A_Index%Fired)
       DemoText=%DemoText%TickCount=%A_TickCount% SetWinTimer(hGui`,%NextTimerID%`,%TimeInMiliseconds%`,Timer%A_Index%Fired) `;Started Timer%A_Index%`n
       ToolTip,%DemoText%,0,0,1
       if(A_Index=1)
           Sleep 1000 ;keep the timers out of sync for demo
   }
   GoSub CheckTimers
   DemoText=%DemoText%`nDone!
return

;Check if timer target variables are set
CheckTimers:
   Count:=0
   Loop
   {
      Loop 2
      {
          If(Timer%A_Index%Fired){
              count++
              Timer%A_Index%Fired:=false
              DemoText=%DemoText%`nTickCount=%A_TickCount% Timer %A_Index% Fired: Total Firings=%count%
              ToolTip,%DemoText%,0,0,1
          }
      } 
      if(count>=6){
         break
      }
   }
   NextTimerID:=0x1000
   ;Kill timers
   ;To kill an individual timer set it's duration to 0
   DemoText=%DemoText%`n
   Loop 2
   {
       TimerID:=TimerID%A_Index%
       SetWinTimer(hGui,TimerID%A_Index%,0,_:="") ;kill the timer
       DemoText=%DemoText%`nTickCount=%A_TickCount% SetWinTimer(hGui`,%TimerID%`,0`,_:="") `;kill the timer
       ToolTip,%DemoText%,0,0,1
   }
   DemoText=%DemoText%`nTickCount=%A_TickCount% Done!
   ToolTip,%DemoText%,0,0,1
   ;we could have done this instead
   ;SetWinTimer(0,0,0,_:="") ;kill all timers by setting hGui=0
   GuiControl,Enable,StartTimers1
   GuiControl,Enable,StartTimers2
return
StartTimers2: ;function target version
    StartTimersFromFunction(hGui)
return
StartTimersFromFunction(hGui)
{
   GuiControl,Disable,StartTimers1
   GuiControl,Disable,StartTimers2
   TimeInMiliseconds:=3000 ;Duration in miliseconds (3 seconds)
   Loop 2
   {
       ;use TimerID=0 feature to get next timer ID (0x1001 and above)
       if(A_Index=2)
          TimeInMiliseconds:=-1*TimeInMiliseconds ;demo single shot timer
       TimerID:=SetWinTimer(hGui,0,TimeInMiliseconds,_:="TimerFired",true)
       ;At this point SetWinTimer has sent our TimerFired
       ;function the new timer ID with use of UserParam1=true
       ;to indicate to TimerFired that a new timer was added.
       ;This avoids use of globals.
       if(A_Index=1)
           Sleep 1000 ;keep the timers out of sync for demo
   }
   return
}
;Function Called when timer fires
;You can use more than 1 function
;Or combine multiple Timer IDs as in this case
TimerFired(ID,UserParam1,UserParam2,UserParam3,UserParam4)
{
    ;SetWinTimer always sends UserParams of 0 when timer fires
    ;When a timer is added it sends Timer ID and UserParam1=1
    ;So that the callback process can do housekeeping.
   Static count,Timers,x,y,DemoText,ClearDemoText
   if(ClearDemoText){
       DemoText:=""
       ClearDemoText:=false
   }
    if(UserParam1=1){ ;UserParam1=1 indicates addition of new timer
DemoText=%DemoText%TickCount=%A_TickCount% TimerID:=SetWinTimer(hGui`,0`,TimeInMiliseconds`,_:="TimerFired"`,true) `;TimerID=%ID%`n
DemoText=%DemoText%TickCount=%A_TickCount% called TimerFired(%ID%`,1`,0`,0`,0) `;Registered Timer with target function`n
       ToolTip,%DemoText%,0,0,1
       if(Timers)
           Timers .=","
       Timers .=ID+0
        ToolTip,%DemoText%,0,0,1
       return 1
   }
   count++
   Loop,Parse,Timers,CSV
   {
       if(ID=A_LoopField){
DemoText=%DemoText%`nTickCount=%A_TickCount% AHK called TimerFired(%A_LoopField%`,0`,0`,0`,0) - Timer %A_Index% Fired: Total Firings=%count%
           ToolTip,%DemoText%,0,0,1
       }
   }
    if(count>=6){
          SetWinTimer(0,0,0,_:="") ;kill all timers
DemoText=%DemoText%`n`nTickCount=%A_TickCount% SetWinTimer(0`,0`,0`,_:="") `;killed all timers
        ToolTip,%DemoText%`nTickCount=%A_TickCount% Done!,0,0,1
        count:=0
        Timers:=""
        ClearDemoText:=true
        GuiControl,Enable,StartTimers1
        GuiControl,Enable,StartTimers2
        return 0
    }
    return 1
}

GuiEscape:
GuiClose:
   ;Kill all timers in case of early exit
   SetWinTimer(0,0,0,_:="")
ExitApp


Here is the script code for the SetWinTimer Function:
Code:
;************************ SetWinTimer ***********************
; Starts/Kills 1 or more timers using DllCalls to
; Windows SetTimer/KillTimer functions. Uses a TimerProc
; Windows callback function so does not interfere with
; timer (WM_TIMER) messages that are being handled with
; the AHK OnMessage function or AHK SetTimer command.
;
; Creates no global variables and requires no labels.
;
; There are 2 ways to use the function:
; 1) With Variable Targets
;    A caller's BYREF variable is set to true when the timer fires.
;        This will require periodic polling of the variable
;        to determine if the timer has fired (variable=true=1)
;
;    SetWinTimer(hGui,ID,Duration,TargetVar:=0)
;
; 2) With Function Targets
;    A caller's function(s) is called when timer(s) fires.
;        This is probably more useful than the above.
;
;    MyTimerCallback(ID,UserParam1,UserParam2,UserParam3,UserParam4)
;
;        This user named function is called whenever a timer fires and
;        whenever a new timer is added.
;
;        The caller's function must accept 5 parameters,
;        the Timer ID (1st param) and 4 int user parameters.
;        You are not required to use the user parameters.
;        However, if you ignore UserParam1 the timer will
;        appear to fire immediately. See below for how to use
;        UserParam1. You may make the 4 UserParams
;        optional. The function is expected to return an int.
;
;        When a new timer is added with
;
;    SetWinTimer(hGui,ID,Duration,_:="MyTimerCallback",IsFunction:=1)
;
;        the callback function is called immediately with UserParam1
;        set to 1 and the Timer ID in ID. This allows the caller to
;        use static variables for tracking timers instead of globals
;        or labels.
;
;        Whenever a timer fires and the function is called, Timer ID
;        will be in ID and all user parameters will contain 0.
;
; A negative (-) duration creates a single shot timer.
; To kill an individual timer set its duration to 0.
; To kill all timers call with a Gui window handle of 0.
;
; Returns 0 for failure
;     or >0 Timer ID for successful addition of timer
;     or 1 for successful timer kill (0 when no timers to kill)
;
;     If there is attempt to access an unknown timer a MsgBox
;     will appear encouraging the user to quit AHK.
;
; Save this as SetWinTimer.ahk in your AutoHotKey user library or
; save it in the same directory as your script and use
; #include SetWinTimer.ahk at thr first of your script.
;
; AutoHotKey version tested: 1.0.47.02
; Revisions:
; 2/4/2008 Replaced WM_Message with TimerProc callback
; 2/6/2008 Added callback function(s) as target for timers
; 5/02/2010 Changed to correct bug with single shot timers.
; Author: dmatch @ AutoHotKey forum 2/3/2008
;**********************************************************************
SetWinTimer(GuiHandle   ;Window's Handle of Gui or 0 to kill all timers.
,ID                     ;Your Timer ID (use 0x1000 and higher).
                        ;Or 0 to use default (last Timer ID + 1).
                        ;Default is 0x1001 when no timers running.
,Duration               ;Timer duration in miliseconds (msec.)
                        ;or 0 to kill the timer (1000 msec.=1 sec.)
,BYREF Target           ;Variable to make true (1) on timeout
                        ;OR string containing function name to call.
,IsFunction=0)          ;If = 1 Target is used as a function name.
{
   Static LastTimerID:=0x1000,TimerProcAddr,Functions
   if(!GuiHandle){ ;Kill all timers
       result:=SetWinTimer_TimerProc(0,0,0,0)
       LastTimerID:=0x1000
       ;Free all memory used for callback addresses
       Loop,Parse,Functions,CSV
           if A_LoopField is integer
               DllCall("GlobalFree",uint,A_LoopField)
       Functions:=""
       return 1
   }
   if(!Duration){ ;Kill the individual timer
      result:=SetWinTimer_TimerProc(-1*GuiHandle,0,ID,2) ;2 indicates to kill
      if(!result){ ;no timers left
          LastTimerID:=0x1000
      }
      return result
   }
   if(ID=0){
       ID:=LastTimerID+1
   }
   LastTimerID:=ID
   If(Duration<0){
       ID:=-1*ID ;Indicates to SetWinTimer_TimerProc a single-shot timer
       Duration:=abs(Duration)
       ;Added above ^^^^^^
   }
   ;Introduce new timer Procs
   if(IsFunction){ ;Register the callback function if needed
       ;See if we already have this function
       if(Pos:=InStr(Functions,Target)){
           DupFunction:=true
           pos:=InStr(Functions,",",0,pos)
           pos++
           pos2:=InStr(Functions,",",0,pos)
           if(!pos2)
               pos2:=StrLen(Functions)+1
           ;Get address we used previously
           TargetAddr:=SubStr(Functions,pos,pos2-pos)
       }
       if(!DupFunction){
           ;Register a new callback target function
           TargetAddr:=RegisterCallback(Target,"",5,0)
           if(TargetAddr){
               if(Functions)
                   Functions .=","
               ;add target and address to function list
               Functions .=Functions . Target . "," . TargetAddr+0
           }
           else
               return 0
       }
       SetWinTimer_TimerProc(-1*GuiHandle,TargetAddr,ID,1)
   }
   Else{
       SetWinTimer_TimerProc(-1*GuiHandle,&Target,ID,0)
   }
   if(!TimerProcAddr)
       TimerProcAddr:=RegisterCallback("SetWinTimer_TimerProc", "", 4, 0)

   ;Start the Timer

result:=DllCall("SetTimer",uint,GuiHandle,uint,abs(ID),uint,abs(Duration),uint,TimerProcAddr)
;changed ID to abs(ID) above ^^^^^^
   return result
}
;*********************************************************
; Timer callback procedure set in SetWinTimer above.
;*********************************************************

SetWinTimer_TimerProc(hWnd,msg,ID,SysTime)
{
   Static TimerIDs
   KillIt:=false
   Result:=0
   HaveError:=0
_SetWinTimer_ReturnOnError_:
   if(hWnd<0){ ;Was internal call from SetWinTimer to introduce/kill timer
      if ID is not integer ;check if contains legit TimerID
         return result
      hGui:=-1*hWnd
      if(SysTime<2){ ;Used to indicate to add a Timer and if is function
         if(TimerIDs)
             TimerIDs .=","
         TimerIDs .=ID+0 . "," . hGui+0 . "," . SysTime+0 . "," . msg+0
         ;We now have a comma delimted string
         ;with "TimerID,hGui,IsFunction,Address".....
         ;send ID to callback process with UserParam1=true
         result:=DllCall(msg+0,int,abs(ID),int,1,int,0,int,0,int,0,int)
;^^^^^ changed line above from ID to abs(ID) ^^^^^^
         return abs(ID) ; - IDs can indicate single-shot timer
      }
      Else{ ; 2 indicates to kill the Timer
         KillIt:=true ;Set switch to Kill the timer (below)
      }
   }
   Else If(!Hwnd){ ;kill all timers
      Loop,Parse,TimerIDs,CSV
      {
          If(Mod(A_Index,4)=1){
              TimerID:=abs(A_LoopField) ;can be -
          }
          Else If(Mod(A_Index,4)=2){
              hWnd:=A_LoopField
              result:=DllCall("KillTimer",uint,hWnd,uint,TimerID,uint)
          }
      }
      TimerIDs:=""
      if(HaveError){ ;HaveError set at end of function
          MsgBox,4,SetWinTimer Error,Attempt to access an unknown timer: TimerID=%ID%`nIt is recommended that you exit.`n`nExit AHK?
          IfMsgBox yes
              ExitApp
      }
      return 0
   }
   if(!TimerIDs) ;No Timers running
      return 0
   ;Comes here when called from windows when timer counts down (fires)
   ;OR when we call internally to kill a timer
   ID:=ID+0 ;make sure it's handled as an integer
   FoundIt:=false
   IsFunction:=false
   
   Loop,Parse,TimerIDs,CSV ;Parse the TimerIDs string
   {
      if(Mod(A_Index,4)=1){ ;Check TimerID
         KillItLater:=false ;Indicator to kill a single-shot timer
         if(ID=abs(A_LoopField)){
            FoundIt:=true
            TimerID:=abs(A_LoopField) ;can be -
            if(A_LoopField<0)
               KillItLater:=true
         }
      }
      Else If(FoundIt and Mod(A_Index,4)=2){ ;get window handle
         hWnd:=A_LoopField+0
      }
      Else if(FoundIt and Mod(A_Index,4)=3){ ;See if it is a function
         if(A_LoopField>0)
            IsFunction:=true
      }
      Else if(FoundIt and Mod(A_Index,4)=0){
         ;Set user's Timer Target Variable to true or call function
         ;A_LoopField contains address of the variable or function
         if(!KillIt){
             if(!IsFunction){
                 NumPut(asc("1"),A_LoopField+0,"char")
                 result:=1
             }
             Else{ ;Call the address registered with RegisterCallback.
                 ;User's callback process must accept 5 params
                 ;The first must be the TimerID
                 ;The other 4 are user params for use in internal calls
                 ;to the callback function.
                 ;The user's callback is expected to return an integer.
                 result:=DllCall(A_LoopField+0,uint,abs(ID),int,0,int,0,int,0,int,0,int)
                 ;Changed ID+0 to abs(ID) above ^^^^^^^^^
             }
             if(!KillItLater)
                 return result
         }
         If(KillIt or KillItLater){ ; Kill the timer
            if(!DllCall("KillTimer",uint,hWnd,uint,TimerID)){
                HaveError:=1
                GoTo _SetWinTimer_ReturnOnError_
            }
            ;remove Timer info from string
            FindTimer:="," . ID . ","
            pos:=InStr(TimerIDs,FindTimer)
            pos++
            StartPos:=pos
            Loop,4
               pos:=InStr(TimerIDs,",",0,pos+1)
               if(!pos) ;reached end of string
                   TimerIDs:=SubStr(TimerIDs,1,StartPos-1) . SubStr(TimerIDs,pos+1)
            if(!TimerIDs){
               return 0 ;indicates to SetWinTimer that no timers are left
            }
            return 1
         }
      }
   }
   ;Shouldn't get to here so must be error
   hWnd:=0
   HaveError:=1
   GoTo _SetWinTimer_ReturnOnError_
}

I don't have any idea how well it might function with more and more timers running or the accuracy that it will attain, so if it breaks something consider yourself forewarned. :wink:

Thanks to lexiKos for the nifty way to use ToolTip to demonstrate things like this. I was tired of using MsgBox with all the beeping.

I haven't came up with a need for this yet so this was an academic exercise that I hope might be of some practical use to someone.

dmatch


Last edited by dmatch on May 2nd, 2010, 4:26 pm, edited 11 times in total.

Report this post
Top
 Profile  
Reply with quote  
PostPosted: February 3rd, 2008, 5:42 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
Would I be correct in assuming you must poll for timer completion (i.e. repeatedly check whether a timer has fired?) This more or less defeats the purpose of having timers, since you could just as well "count down" in a loop.
Quote:
I wrote this function that uses no globals or labels
True, but it does globally reserve the function name "WM_TIMER", and prevent any other script's from detecting WM_TIMER while it is running...

Interesting solution, nonetheless.


Report this post
Top
 Profile  
Reply with quote  
PostPosted: February 4th, 2008, 7:06 pm 
Offline

Joined: October 15th, 2007, 7:23 pm
Posts: 252
lexiKos wrote:
Would I be correct in assuming you must poll for timer completion (i.e. repeatedly check whether a timer has fired?) This more or less defeats the purpose of having timers, since you could just as well "count down" in a loop.
Quote:
I wrote this function that uses no globals or labels
True, but it does globally reserve the function name "WM_TIMER", and prevent any other script's from detecting WM_TIMER while it is running...

Interesting solution, nonetheless.
Yes the loop (polling) is needed to check timer fired status, but there could be other things going on in this loop depending on what a person wanted to do. It doesn't have to be dedicated to just the timers. For instance, the loop could call a function and check the timer fired variable there too. Anyway, the polling loop is not unlike a main window message loop in a Windows Application and many things could be done from it.

I agree it has limited function.

Edited to Add: I think I will get rid of the WM_TIMER message handler. That shouldn't be too hard to do.

dmatch


Report this post
Top
 Profile  
Reply with quote  
 Post subject: Updated First Post
PostPosted: February 6th, 2008, 7:46 pm 
Offline

Joined: October 15th, 2007, 7:23 pm
Posts: 252
First post was updated with new code that does not trap WM_TIMER messages and will also call a function(s) when a timer(s) fires.

This makes it really global-less.

dmatch


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 9th, 2008, 10:39 pm 
Offline

Joined: May 28th, 2007, 4:09 pm
Posts: 8
Thank you dMatch

this will come in handy

-Basi


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 30th, 2010, 4:01 pm 
Offline

Joined: April 29th, 2010, 3:56 pm
Posts: 8
Is it possible to use this function without the need of creating a gui for the handle?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 30th, 2010, 8:58 pm 
Offline

Joined: October 15th, 2007, 7:23 pm
Posts: 252
I don't think so. The Windows functions that are called require a window handle. However, you could display the GUI off-screen like:
Code:
Gui, +Toolwindow
Gui, Show, y10000,Testing
hGui:=WinExist("Testing")
ToolTip, `n`n`n`nWindow handle is ... %hGui%`n`n`n`n
sleep, 3000
ToolTip
ExitApp

Hope this helps,

dmatch


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 1st, 2010, 6:15 pm 
Offline

Joined: April 29th, 2010, 3:56 pm
Posts: 8
Thank you, I have been doing a similar thing with WinHide.

As for running the timer once, using a negative duration, the timer only fires initially when setting UserParam1 to 1, and does not fire again when the duration is finished.
Below is my code for firing the timer. Is there anything that I am doing wrong?
Code:
Gui, +Toolwindow
Gui, Show, y10000, Testing
hGui := WinExist("Testing")

SetWinTimer(hGui, 2000, 5000, _:="TimerFunc", IsFunction:=1)    ; calls initially and every 5 seconds after that
;SetWinTimer(hGui, 2000, -5000, _:="TimerFunc", IsFunction:=1)  ; only calls initially

TimerFunc(ID, UserParam1, UserParam2, UserParam3, UserParam4) {
   If (UserParam1 = 1)
      MsgBox, Timer has started.
   Else
      MsgBox, Timer has fired.
}


In your DllCall to SetTimer, Duration is still negative so is that a problem?
Thanks again for your help.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 2nd, 2010, 4:04 pm 
Offline

Joined: October 15th, 2007, 7:23 pm
Posts: 252
Found the bug in the function, which caused the problem with single shot timers that you described. Thanks for pointing out the problem. I believe it was the - Duration as you said (and another problem too).

Updated the demo to include single shot timers and corrected the SetWinTimer function (1st post above).

Give it another try now. The demo works for single shot timers now.

dmatch


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 2nd, 2010, 9:28 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
For similar function, check Forms/_ :d() (delay function). The function doesn't influence your script in any way.

Quote:
Is it possible to use this function without the need of creating a gui for the handle?

You can use d() without GUI.

Since d() works like negative timer, to achieve repeatable timer you can use something like:

Code:
#persistent
_()

d("go", 1000, "ej", "there")

go(a1,a2)
{
   m(a1, a2)
   d(A_ThisFunc, 1000, a1, a2)
}



Code:
d(fun, delay="", a1="", a2="" ) {
   static adrSetTimer, adrTimerProc
   if !adrSetTimer
   {
      adrSetTimer := DllCall("GetProcAddress", uint, DllCall("GetModuleHandle", str, "user32"), str, "SetTimer")
      adrTimerProc := RegisterCallback("d_","", 4)
      d_( "adrKillTimer", DllCall("GetProcAddress", uint, DllCall("GetModuleHandle", str, "user32"), str, "KillTimer"))
   }

   Old_IsCritical := A_IsCritical
   critical 1000
   id := DllCall(adrSetTimer, "uint", 0, "uint", 0, "uint", delay, "uint", adrTimerProc)
   d_(id, fun), d_(id "_1", a1), d_(id "_2", a2)
   Critical %Old_IsCritical%
}

d_(hwnd, msg, id="", time=""){
   static
   ifEqual, id, , return %hwnd% := msg
   DllCall(adrKillTimer, "uint", 0, "uint", id)
   fun := %id%, %fun%( %id%_1, %id%_2 ), %id%_1 := %id%_2 := ""
}

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 3rd, 2010, 2:43 am 
Offline

Joined: April 29th, 2010, 3:56 pm
Posts: 8
dmatch wrote:
Found the bug in the function, which caused the problem with single shot timers that you described. Thanks for pointing out the problem. I believe it was the - Duration as you said (and another problem too).

Updated the demo to include single shot timers and corrected the SetWinTimer function (1st post above).

Give it another try now. The demo works for single shot timers now.

Glad I could be of help, and thank you for your quick replies and your fix.


majkinetor wrote:
You can use d() without GUI.

Thank you for pointing this out as well.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Stigg and 10 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group