The problem is not exclusive to ListViews.
AutoHotkey handles most messages within an internal function called MsgSleep. MsgSleep retrieves messages from our message queue, handles or dispatches them, and (usually) sleeps.
When an event occurs, AutoHotkey posts a message to itself. Normally MsgSleep will then start a new thread and call a subroutine or function, after processing any other messages that preceded the event in the queue.
If the current thread is uninterruptible, MsgSleep filters messages so that most system-defined messages can be processed, while event messages and user messages are left in the queue until the thread becomes interruptible. (This is the "event buffering" that the documentation for Critical refers to.)
When you display a modal dialog or Menu, or perform some other modal GUI action (such as click and drag in a ListView or activating a MonthCal's drop-down), the program enters a modal message loop
. That is, some code outside our control runs in a loop, retrieving messages from the queue and dispatching them or handling them directly. This defeats the filtering done by MsgSleep, since MsgSleep isn't the one processing the message queue. When an event message is dispatched to the script's main window procedure or GUI window procedure, it is posted back into the queue and MsgSleep is called to immediately process all messages and handle the event.
However, if the current thread is uninterruptible, MsgSleep will neither handle the message nor remove it from the queue. Therefore, when control returns to the modal message loop
, the message is again dispatched to our window procedure, and we again post it back into the queue and call MsgSleep, and so on. This can become an infinite loop.
The problem occurs under these conditions:
- A modal message loop is running.
- An event has been posted to the queue which is eligible to be re-posted.
- The script is uninterruptible.
- Menu command messages sent to the script's main window are re-posted unconditionally.
- GUI events and menu command messages sent to a GUI window are re-posted and MsgSleep is called, but not if a menu is being displayed (in that case they are discarded).
- Hotkey, hotstring, OnClipboardChange and InputHook End events are always re-posted but MsgSleep is not called if the thread is uninterruptible (and since they're posted with a NULL hWnd, it's very likely that the message will be discarded by the modal message loop).
- Timer messages are not re-posted.
As documented, Critical
makes the current thread uninterruptible, but is temporarily overridden when a MsgBox or other dialog is displayed. However, other conditions that temporarily disable interruption are not affected by dialogs:
- Running OnExit functions.
- The internal SLEEP_WITHOUT_INTERRUPTION macro, which is used by Send when performing delays, and for other very small delays by various commands (e.g. after changing the foreground window, to let the change be processed).
- While displaying the #MaxHotkeysPerInterval error message or failure to activate the hooks.
- While a menu is being displayed or a menu bar has the focus (but this also prevents GUI events from being re-posted).
In your case, MsgBox begins running a modal message loop and at the same time generates a ListView focus-lost event. The script is uninterruptible because it is running an OnExit function, so the focus-lost event "bounces" between the message queue and the GUI window procedure, inhibiting the MsgBox's normal initialization and preventing it from appearing.
This also demonstrates the problem:
Code: Select all
ListLines ; One of the script's own windows is active.
Menu M, Add, Clickme
Menu M, Show ; An event is queued.
MsgBox,,,, 3 ; MsgBox is displayed while the thread is uninterruptible.
If none of the script's windows are active, this has no problem. I'm guessing it has to do with additional messages being in the queue when the window loses focus.
should fix it, by causing GUI and menu events to be discarded when they can't be handled or kept in the queue.