Jump to content

Are timers bugged? Watch this example


marko29
 Share

Recommended Posts

If you press button 1 it will normally start a while loop and when you press button 2 the msg will be invoked and show a consolewrite. BUT

If the button 1 function starts with the timer, it will completely block everything, as well as message that is sent to gui with button 2.

Also note that hotkey is completely ignored/doesnt trigger if this function starts with timer.

After days and days of debugging i came to this and made up short script to show it. I really need timers to trigger my function that needs to receive message but how if messages are blocked when function starts with timer?

Could it be that timers are perhaps started on another thread?

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Timers.au3>


#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

; Set a HotKey
HotKeySet("x", "_Interrupt")

; Declare a flag
$fInterrupt = 0
$started = 0
$hGUI = GUICreate("Test", 500, 500)

$hButton_1 = GUICtrlCreateButton("Func One", 10, 10, 80, 30)
$hButton_2 = GUICtrlCreateButton("Func Two", 10, 50, 80, 30)

; Create a dummy control for the Accelerator to action when pressed
$hAccelInterupt = GUICtrlCreateDummy()
; Set an Accelerator key to action the dummy control
Dim $AccelKeys[1][2]=[ ["z", $hAccelInterupt] ]
GUISetAccelerators($AccelKeys)

GUISetState()

; Intercept Windows command messages with out own handler
GUIRegisterMsg($WM_COMMAND, "_WM_COMMAND")

$timer = _Timer_SetTimer($hGUI, 10000, "timerfunc")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
        Case $hButton_1
            _Func_1()
        Case $hButton_2
            _Func_2()
    EndSwitch
WEnd

Func _Func_1()

    $started = 1
    while 1
        ConsoleWrite("Sleeping..."&@LF)
        Sleep(2000)
    WEnd

EndFunc

Func _Func_2()

    ConsoleWrite(">Func 2" & @CRLF)
EndFunc

Func _Interrupt()
    ; The HotKey was pressed so set the flag
    $fInterrupt = 2
EndFunc

Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam)
    ; The Func 2 button was pressed so set the flag
    If BitAND($wParam, 0x0000FFFF) =  $hButton_2 Then ConsoleWrite("+BUTTON 2 PRESSED" & @CRLF)
    ; The dummy control was actioned by the Accelerator key so set the flag
    If BitAND($wParam, 0x0000FFFF) =  $hAccelInterupt Then $fInterrupt = 3
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_COMMAND

Func timerfunc($hWnd, $Msg, $iIDTimer, $dwTime)
    #forceref $hWnd, $Msg, $iIDTimer, $dwTime
    Switch _Timer_GetTimerID($iIDTimer)

        Case $timer
            if $started = 1 Then Return
            _Func_1()
    EndSwitch
    ; Make sure the flag is cleared


EndFunc   ;==>timerfunc
Edited by marko29
Link to comment
Share on other sites

While an event's function is running, additional events are blocked. Event handling functions should never have blocking functions (like MsgBox), Sleep(), or long loops in them. Have your event function change the state of something, like a global flag variable, then your main loop can react to that while other events can be triggered.

Even worse, the While/WEnd loop in your _Func_1() function has no exit, and it gets called from timerfunc(), so that function never returns either.

:)

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

PsaltyDS beat me to it. Essentially the timer blocks until the execution of the function it is calling is finished. So the solution is to trigger the function in the main loop. I've done it with a dummy control where you can read the state, so you can trigger any number of functions.

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Timers.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

; Declare a flag
$started = 0
$hGUI = GUICreate("Test", 500, 500)

$hButton_1 = GUICtrlCreateButton("Func One", 10, 10, 80, 30)
$hButton_2 = GUICtrlCreateButton("Func Two", 10, 50, 80, 30)
$hFromTimer = GUICtrlCreateDummy()

; Create a dummy control for the Accelerator to action when pressed
$hAccelInterupt = GUICtrlCreateDummy()
; Set an Accelerator key to action the dummy control
Dim $AccelKeys[1][2]=[ ["z", $hAccelInterupt] ]
GUISetAccelerators($AccelKeys)

GUISetState()

; Intercept Windows command messages with out own handler
GUIRegisterMsg($WM_COMMAND, "_WM_COMMAND")

$timer = _Timer_SetTimer($hGUI, 10000, "timerfunc")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
        Case $hButton_1
            _Func_1()
        Case $hButton_2
            _Func_2()
        Case $hFromTimer
            Switch GUICtrlRead($hFromTimer)
                Case 1
                    _Func_1()
            EndSwitch
    EndSwitch
WEnd

Func _Func_1()
    $started = 1
    while 1
        ConsoleWrite("Sleeping..." & @CRLF)
        Sleep(2000)
    WEnd
EndFunc

Func _Func_2()
    ConsoleWrite(">Func 2" & @CRLF)
EndFunc

Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam)
    ; The Func 2 button was pressed so set the flag
    If BitAND($wParam, 0x0000FFFF) = $hButton_2 Then ConsoleWrite("+BUTTON 2 PRESSED" & @CRLF)
    ; The dummy control was actioned by the Accelerator key so set the flag
    If BitAND($wParam, 0x0000FFFF) = $hAccelInterupt Then ConsoleWrite("Accelerated" & @CRLF)
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_COMMAND

Func timerfunc($hWnd, $Msg, $iIDTimer, $dwTime)
    #forceref $hWnd, $Msg, $iIDTimer, $dwTime
    Switch _Timer_GetTimerID($iIDTimer)
        Case $timer
            if $started = 1 Then Return
            ConsoleWrite("Starting _Func_1() from timer..." & @CRLF)
            GUICtrlSendToDummy($hFromTimer, 1)
    EndSwitch
    ; Make sure the flag is cleared
EndFunc   ;==>timerfunc
Link to comment
Share on other sites

While an event's function is running, additional events are blocked. Event handling functions should never have blocking functions (like MsgBox), Sleep(), or long loops in them. Have your event function change the state of something, like a global flag variable, then your main loop can react to that while other events can be triggered.

Even worse, the While/WEnd loop in your _Func_1() function has no exit, and it gets called from timerfunc(), so that function never returns either.

:)

I am thinking if adlibregister would be proper place to do the looping then. btw i didnt add exit as it was just example script to show what i meant(invoking mssg)

Link to comment
Share on other sites

PsaltyDS beat me to it. Essentially the timer blocks until the execution of the function it is calling is finished. So the solution is to trigger the function in the main loop. I've done it with a dummy control where you can read the state, so you can trigger any number of functions.

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Timers.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

; Declare a flag
$started = 0
$hGUI = GUICreate("Test", 500, 500)

$hButton_1 = GUICtrlCreateButton("Func One", 10, 10, 80, 30)
$hButton_2 = GUICtrlCreateButton("Func Two", 10, 50, 80, 30)
$hFromTimer = GUICtrlCreateDummy()

; Create a dummy control for the Accelerator to action when pressed
$hAccelInterupt = GUICtrlCreateDummy()
; Set an Accelerator key to action the dummy control
Dim $AccelKeys[1][2]=[ ["z", $hAccelInterupt] ]
GUISetAccelerators($AccelKeys)

GUISetState()

; Intercept Windows command messages with out own handler
GUIRegisterMsg($WM_COMMAND, "_WM_COMMAND")

$timer = _Timer_SetTimer($hGUI, 10000, "timerfunc")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
        Case $hButton_1
            _Func_1()
        Case $hButton_2
            _Func_2()
        Case $hFromTimer
            Switch GUICtrlRead($hFromTimer)
                Case 1
                    _Func_1()
            EndSwitch
    EndSwitch
WEnd

Func _Func_1()
    $started = 1
    while 1
        ConsoleWrite("Sleeping..." & @CRLF)
        Sleep(2000)
    WEnd
EndFunc

Func _Func_2()
    ConsoleWrite(">Func 2" & @CRLF)
EndFunc

Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam)
    ; The Func 2 button was pressed so set the flag
    If BitAND($wParam, 0x0000FFFF) = $hButton_2 Then ConsoleWrite("+BUTTON 2 PRESSED" & @CRLF)
    ; The dummy control was actioned by the Accelerator key so set the flag
    If BitAND($wParam, 0x0000FFFF) = $hAccelInterupt Then ConsoleWrite("Accelerated" & @CRLF)
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_COMMAND

Func timerfunc($hWnd, $Msg, $iIDTimer, $dwTime)
    #forceref $hWnd, $Msg, $iIDTimer, $dwTime
    Switch _Timer_GetTimerID($iIDTimer)
        Case $timer
            if $started = 1 Then Return
            ConsoleWrite("Starting _Func_1() from timer..." & @CRLF)
            GUICtrlSendToDummy($hFromTimer, 1)
    EndSwitch
    ; Make sure the flag is cleared
EndFunc   ;==>timerfunc

Thanks a ton! Gonna play with this now
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...