Sign in to follow this  
Followers 0
ako673de

Having the tray menu open always pauses the script

17 posts in this topic

Activating a script's tray menu seems to be enough to pause execution of it

This happens not only in the default mode (Opt("TrayAutoPause",1), but also, where none of the regular tray items (pause/exit) is present. Not to be misunderstood, I know that with "Opt("TrayAutoPause",0)" klicking right on the autoit tray menu normally shows a box that contains 2 tray items (pause/exit), and that pause - when clicked - will pause the script. What I want to say is, that the script even pauses if just this box is being displayed! The whole program is waiting! I don't know why it should do so, isn't that what only the "pause" item should do - after being clicked???

I think it's easy to imagine applications where it is mandatory that a program keeps running all the time (e.g. because it has to processes timing critical operations) but at least, if really needed to have a "pause" functionality, it shouldn't be so easy (just one click) to unintentionally activate it.

So in my opinion this is a show stopping bug for the use of AutoIt. However for some reason I'm not allowed to post in the bug forum so I hope for a solution (or workaround) from this forum.

AKo

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

I'm not so sure that it is a bug, though, perhaps it is. Have you tried with TrayOnEventMode?

Edited by FreeFry

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Happens in all modes. If the box containing the tray items (like "exit" and "pause" in default mode) is beeing displayed the script is on halt, regardless of any AutoIt option (I am aware of).

Please explain why you think that such behaviour is not a bug. Imagine the following:

- The script is designed to wait for e.g. a notification window to appear

- Now the user presses the right mouse button over the tray icon to just look what is behind

- in the meantime the notification window appears and then disappears

- the user has seen what he wanted to see and clicks somewhere on the desktop to remove the tray menu

- the program continues running but the event is missed and so it is lost in an endless waiting loop

Do you really think this is acceptable?!? Disabling the tray menu completely is currently the only solution to avoid such misbehaviour.

Here is a sample script (it uses "ConsoleWrite" to show you when the script is running and when not):

#include <GUIConstants.au3>
#NoTrayIcon
Opt("TrayOnEventMode"   ,1)
Opt("TrayMenuMode"      ,1)
Opt("TrayAutoPause"     ,0)

TraySetOnEvent($TRAY_EVENT_PRIMARYDOUBLE,"TR_Event_Configure")
TrayCreateItem("Configure",-1)
TrayItemSetOnEvent(-1,"TR_Event_Configure")
TrayCreateItem("Stop record",-1)
TrayItemSetOnEvent(-1,"TR_Event_Stop")
TrayCreateItem("",-1)
TrayCreateItem("Exit",-1)
TrayItemSetOnEvent(-1,"TR_Event_Exit")
TraySetState()

while 1
    ConsoleWrite("x")
    Sleep(500)
WEnd

Func TR_Event_Stop()
    MsgBox(0,"Info","Stop clicked")
EndFunc
Func TR_Event_Exit()
    MsgBox(0,"Info","Exit clicked")
    Exit
EndFunc
Func TR_Event_Configure()
    MsgBox(0,"Info","Configure clicked")
EndFunc
Edited by ako673de

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

No, I didn't say that I thought it was acceptable, I just wanted to try it out on my computer too, to see if I could replicate it..

It has the same effect for me, somone should probably file a bug report..

Edit:

Try this:

Create your own tray items, and see if it still pauses.. might work as a work around temporarily..

Edited by FreeFry

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

As long as the menu (or "box" as you call it) is visible, yes the script will be paused with any option you choose. I believe this was brought up before, and is a limitation of the program. The same would happen if you had a GUI in your script and you right clicked on the title bar, everything will pause until the menu disappears.

Disabling the tray menu completely is currently the only solution to avoid such misbehaviour.

That is correct.

As for your issue about posting to the bug forum, that is no longer enabled as the developers are using AutoIt Trac to manage bug reporting now. If you feel the need, and the bug report does not already exist, create a bug report there, but please be sure to follow the standards for bug reports as stated in this thread.

*Edit: Submitted instead of previewing before I was ready

Edited by Saunders

Share this post


Link to post
Share on other sites

@FreeFry: What exactly do you mean with "create my own tray items"? My sample script (in my opinion) ONLY uses "own tray items". Are we talking about the same ;)? Or did you miss my Edit to the last post?

@Saunders: A limitation of the program? Man, that is quite some limitation :P !!! My script needs the ability to have a tray icon. And it needs to run ALL THE TIME. Any idea how to still enable me to use AutoIt?

You say the same problem is for GUIs. Oh my god! Solution for this? Not use GUIs any more?

Recently I wrote an "Active" MsgBox() routine. The only intention for me to write it was to have a MsgBox() that does not pause the running program (can be polled in a loop that performs periodic tasks). It is and was mandatory for me to have such. Now you tell me, that the user still can unintentionally pause these periodic tasks by clicking on certain boxes in my MsgBox GUI? Now that I know about this "limitation" it took only a few clicks to crash my script :D and I have no idea how to avoid it. 4 months of work are in vain, because that makes AutoIt completely useless for my project :P.

What exactly is the reason for these limitations? None of the programs I know of have such limitations. Isn't the window and tray system completely event based? Does AutoIt use other methods to implement this? Why???

Share this post


Link to post
Share on other sites

I meant creating your own tray items with TrayCreateMenu() and TrayCreateItem(), we're probably talking about the same thing...

On a side note:

I belive the cultprit behind these limitations is that AutoIt is a SingleThreaded language(I don't know if this is the case of trayitems though, as AutoIt uses some MultiThreading for some built-in functions)..

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

Oh my god! Solution for this? Not use GUIs any more?

<...>

Now that I know about this "limitation" it took only a few clicks to crash my script :D and I have no idea how to avoid it. 4 months of work are in vain, because that makes AutoIt completely useless for my project :P.

I can imagine situations when such features are unacceptable, however I really don't see it as such a "show stopping" big deal as you make it out to be.

If you need to do some task that is so prone to interruption, can't you have it as a separate "invisible" script, and have another (main) script for the user to interact with. Exchanging data between the two processes, if there's need for that, is possible by sending user messages, setting data with ControlSetText or various UDFs, and other ways.

Blocking mouse and keyboard for short period of time while you do something that could be messed up by user input is an option too.

Edited by Siao

"be smart, drink your wine"

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

@Saunders: A limitation of the program? Man, that is quite some limitation :P !!! My script needs the ability to have a tray icon. And it needs to run ALL THE TIME. Any idea how to still enable me to use AutoIt?

I am by no means an AutoIt expert, long time user sure, but no expert. I could be mistaken, I'm basing what I say on memory, so don't give up hope. One option I can think of at the moment is to launch a separate process to do the monitoring, no tray icon, no GUI, just the window monitoring. The only way a user could cock that up would be via Task Manager. Then you just handle that extra process with the original process that has your tray icons, GUI, etc.

I even wrote up this code to demonstrate (took me like 15 minutes, but it IS almost 5am after all).

#include <GUIConstants.au3>

Opt('MustDeclareVars', 1)

Global $bMonitoring = False, $sMonitorName, $sExternalCmd
Global $gm, $hGui, $bt_Quit

If $CmdLine[0] >= 2 Then
    If $CmdLine[1] = 'startmonitor' And $CmdLine[2] <> '' Then
        $bMonitoring = True
        $sMonitorName = $CmdLine[2]
    EndIf
EndIf

If $bMonitoring Then ; Monitor process code
    Opt('TrayMenuMode', 1) ; No default menu
    TraySetToolTip('Monitoring process')
    AutoItWinSetTitle($sMonitorName)
    While 1
        $sExternalCmd = ControlGetText($sMonitorName, '', 1)
        Switch $sExternalCmd
            Case 'QUIT'
                Exit
            Case 'OTHER'
                ; Do something else
            Case 'ETC'
                ; Etc... just to show you some options
            Case Else
                If $sExternalCmd <> '' Then ControlSetText($sMonitorName, '', 1, '')
        EndSwitch

        If WinExists('Whatever you want') Then
            ; Etc, this is where you put your main window monitoring stuff
        EndIf
        
        ToolTip('I am a tooltip from the monitoring process. ' & @SEC, 50, 50)
    WEnd
Else ; GUI process code
    TraySetToolTip('GUI process')
    Do
        $sMonitorName = 'Monitor:' & Random() ; Ensures we have a unique window name
    Until Not WinExists($sMonitorName)
    If @Compiled Then
        Run(@AutoItExe & ' startmonitor "' & $sMonitorName & '"')
    Else
        Run(@AutoItExe & ' "' & @ScriptFullPath & '" startmonitor "' & $sMonitorName & '"')
    EndIf

    $hGui = GUICreate('Some program', 200, 200)
    $bt_Quit = GUICtrlCreateButton('Quit', 10, 10, 100, 25)
    GUISetState()

    While 1
        ToolTip('I am a tooltip from the GUI process. ' & @SEC, 50, 30)
        $gm = GUIGetMsg()
        Switch $gm
            Case $bt_Quit
                ControlSetText($sMonitorName, '', 1, 'QUIT')
            Case $GUI_EVENT_CLOSE
                ControlSetText($sMonitorName, '', 1, 'QUIT')
                ExitLoop
        EndSwitch
    WEnd
EndIf

*Edit: My code here is basically an example of exactly what Siao touched on in his reply.

*Edit2: Forgot to declare a variable. :D

Edited by Saunders

Share this post


Link to post
Share on other sites

WOW! That is great. So many commands I didn't even know that they exist (AutoItSetWinTitle, $cmdline, ...). Thank you very much for this. I just built a slightly modified version of it into my script and can confirm, that the concept works!

Just one problem remains: Your example uses polling in the process where pausing is inacceptable to receive messages from the pausable process.

Unfortunately, as I mentioned, in my application the main loop is VERY timing critical. It must use as less cpu power as possible ("sleep()" period > 5sec) WHILE not reacting too slow on tray-clicks (handled by the other process).

Is there a way to let the processes communicate in a way that events (=interrupts) are being generated in the message receiving process?

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Example with DllCallBackRegister()

#include <GUIConstants.au3>
#NoTrayIcon

Opt("TrayOnEventMode"   ,1)
Opt("TrayMenuMode"      ,1)
Opt("TrayAutoPause"  ,0)

Global $TimerProc, $iTimer

TraySetOnEvent($TRAY_EVENT_PRIMARYDOUBLE,"TR_Event_Configure")
TrayCreateItem("Configure",-1)
TrayItemSetOnEvent(-1,"TR_Event_Configure")
TrayCreateItem("Stop record",-1)
TrayItemSetOnEvent(-1,"TR_Event_Stop")
TrayCreateItem("",-1)
TrayCreateItem("Exit",-1)
TrayItemSetOnEvent(-1,"TR_Event_Exit")
TraySetState()

$TimerProc = DllCallbackRegister("TimerFunc", "int", "")
$iTimer = DllCall("User32.dll", "int", "SetTimer", "hwnd", 0, "int", 0, "int", 500, "ptr", DllCallbackGetPtr($TimerProc))

while 1
    Sleep(100)
WEnd

Func TR_Event_Stop()
    MsgBox(0,"Info","Stop clicked")
EndFunc
Func TR_Event_Exit()
    DllCallbackFree($TimerProc)
    DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $iTimer[0])
    MsgBox(0,"Info","Exit clicked")
    Exit
EndFunc
Func TR_Event_Configure()
    MsgBox(0,"Info","Configure clicked")
EndFunc

Func TimerFunc()
    ConsoleWrite("x")
EndFunc
Edited by rasim

Share this post


Link to post
Share on other sites

I don't exactly understand what you're trying to show me. As far as I understand your code, the big difference is that the periodic task is "event-ized".

But that does not solve anything, because the problem still persists, that, if you right click on the tray icon, a menu will pop up, and while it is showing the script is on halt - COMPLETELY! That also concerns event functions because these are also just part of the script.

What I was asking for is an event solution for the "multi-process" example by Saunders. His solution works by sending messages over the script internal AutoIt window, but the controls in this window can't be made event-able (because they are not accessible by the GUICtrlSetOnEvent() function) - well, at least after all of MY investigations...

Share this post


Link to post
Share on other sites

if you right click on the tray icon, a menu will pop up, and while it is showing the script is on halt - COMPLETELY!

Seriously? Then say me, why function TimerFunc() work regardless of that a menu will pop up and messages in MsgBox? I don`t know completely what you need, i`m just show example. If this example don`t need for you, then forgot...

:D

Share this post


Link to post
Share on other sites

Seriously? Then say me, why function TimerFunc() work regardless of that a menu will pop up and messages in MsgBox? I don`t know completely what you need, i`m just show example. If this example don`t need for you, then forgot...

:D

I must admit that I didn't run your code (because I can't from where I am currently). So my first guess was just that the event code is part of the script, and what I know about that is, that it does not work. But now I think I got your idea!!! You assume, that the timer function of USER32.DLL already IS a separate thread (which does not depend on the run state of my script but just uses some part of it's code), right?

That is a so far better solution!!! Thank you. I will give it a try as soon as I can.

Share this post


Link to post
Share on other sites

Yep! And see here

:D

Share this post


Link to post
Share on other sites

Rework complete. Works like a charm, even better than expected! Memory usage of the ONE process is at 1.4MB while the solution with two script processes (by Saunders) took 7+9MB (someone knows where this difference might come from? I'm not aware of any larger changes in variable count...). Furthermore the process does not use any noticable cpu time. The two-process-solution once in a while had load peaks up to 4% (each second, which equals the sleep timer used).

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

Rework complete. Works like a charm, even better than expected! Memory usage of the ONE process is at 1.4MB while the solution with two script processes (by Saunders) took 7+9MB (someone knows where this difference might come from? I'm not aware of any larger changes in variable count...). Furthermore the process does not use any noticable cpu time. The two-process-solution once in a while had load peaks up to 4% (each second, which equals the sleep timer used).

I have an update to report! My script now worked for several months and I didn't notice any problems, well, except of some "spurious" once in a while. Wasn't easy to find, but I just figured it out:

Everything works like a charm, if always the regular program is running when clicking the tray icon. Timer routines are normally not so time consuming and therefore this case is highly probable. In case of your "consolewrite("x")"-sample script the probability of clicking the tray icon while the "x" is being written to the console is <1ms/500ms (<0.2%!).

But if you really are so unlucky to click the tray icon while the timer routine is still executing, then the timer routine will be stopped and later timer events will also not get processed - until you release the tray icon.

This behaviour is a little bit in contrast to the opinion we all had (=that the callback timer is a separate thread itself), isn't it?

Can anybody explain this behaviour? How could the timer callback interact with the main Autoit thread in such way? Shouldn't it just use the code from the AutoIt-script and nothing else?

Edited by ako673de

Share this post


Link to post
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
Sign in to follow this  
Followers 0