class EventHook extends Object
The base class of every event subscription a script holds. A script never creates one directly; each kind of hook is its own subclass:
| Class | Created by | Initial state |
|---|---|---|
| WinEvent | WinEvent(WinTitle, WinText, ExcludeTitle, ExcludeText) | Idle until Start() |
| MonitorHook | Monitor.OnChange | Running |
| ClipboardHook | Clipboard.OnChange | Running |
| Audio.DeviceHook | Audio.OnDeviceChange | Running |
| Clr.EventSubscription | OnEvent on a CLR object or type | Running |
| InputHook | InputHook() | Idle until Start() |
This class is exported by the KS module.
| Member | Description |
|---|---|
InProgress | 1 while the hook is running, so that its callback is called when the event occurs; otherwise 0. Read-only. |
EndReason | An empty string while the hook is running; otherwise why it is not. Read-only. |
Start() | Begins a new run. It does nothing to a hook which is running. |
Stop() | Ends the run with EndReason "Stopped" and releases its native event source. It does nothing to a hook which is not running. |
Some subclasses add members of their own, such as a WinEvent's event slots and a CLR subscription's EventName and Target; MonitorHook, ClipboardHook and Audio.DeviceHook add none.
A hook's callback is checked when it is given: a value which is not an object raises a TypeError, an object which cannot be called raises a MethodError, and a callback which cannot take the arguments the hook passes raises a ValueError.
InProgress describes the subscription, not what the platform delivers. An event source which installed but reports nothing — a window event the platform does not expose, for example — leaves the hook reading 1.
When a hook is not running, EndReason is one of these, the first reason its last run ended for:
| EndReason | Meaning |
|---|---|
"Stopped" | Stop() was called, or the hook has not been started yet. |
"Exit" | The real thread which started the run ended, or the script is exiting. |
"Failed" | The native event source could not be installed, for example because a platform component it needs is missing. Monitor.OnChange, Clipboard.OnChange and Audio.OnDeviceChange return the hook already ended in this case, and Start() leaves a hook ended in the same way. OnEvent on a CLR object or type, and Start() on its subscription, raise an error instead. |
InputHook reports its own end reasons instead, which include "Stopped" and "Failed" but not "Exit".
On Windows, for Notepad, a hook reads as not running until it is started:
#Import "Ks" { WinEvent }
Hook := WinEvent("ahk_exe notepad.exe")
Hook.OnActive := (Hook, Hwnd, Time) => ToolTip("Notepad is active")
MsgBox Hook.InProgress " [" Hook.EndReason "]" ; 0 [Stopped]
Hook.Start()
MsgBox Hook.InProgress " [" Hook.EndReason "]" ; 1 []
Hook.Stop()
MsgBox Hook.InProgress " [" Hook.EndReason "]" ; 0 [Stopped]
Start() begins a fresh run of a hook which is not running, including one which has been stopped:
Monitor.OnChange, Clipboard.OnChange, Audio.OnDeviceChange or OnEvent keeps its callback and filter, so it runs as it did before.An event which occurred while the hook was not running is not reported. The callbacks of a run are called on the real thread which started it: the one which called Start() or the subscribing method. An InputHook is the exception: each of its callbacks is called on the real thread which assigned it, or on the main thread once that thread has ended.
Stop() takes effect when it is called. An event which has already arrived but whose callback has not started is discarded, and a callback which is already running finishes. A discarded event is not reported by a later Start(). For how an InputHook differs, see its Stop method.
Call Stop() as the first statement of the callback. On Windows, for Notepad:
#Import "Ks" { WinEvent }
Hook := WinEvent("ahk_exe notepad.exe")
Hook.OnActive := OnNotepad
Hook.Start()
OnNotepad(Hook, Hwnd, Time) {
Hook.Stop()
MsgBox "Notepad was activated."
}
A second event which arrived before the callback started is then discarded. The callback can still run twice if it waits before stopping — with Sleep, MsgBox, WinWait or anything else which lets another thread run — because the next event's callback can start during that wait. Guard such a callback with a static flag.
A run lasts until Stop(), until the real thread which started it ends, or until the script exits. Letting go of the hook object does not stop it, so the return value need not be kept; the class's Hooks list still reaches it.
An InputHook's Input does not end when a real thread ends.
Whether a running hook keeps the script running:
| Hook | Keeps the script running |
|---|---|
| WinEvent | Yes |
| Clipboard.OnChange | Yes |
| InputHook | Yes |
| Monitor.OnChange | No |
| Audio.OnDeviceChange | No |
| OnEvent on a CLR object or type | No |
A script which responds only to hooks of the second kind calls Persistent to keep waiting for events. Such a hook started on a real thread does not keep that thread running either.
Hooks := WinEvent.Hooks Hooks := Monitor.Hooks Hooks := Clipboard.Hooks Hooks := Audio.Hooks Hooks := Clr.Hooks
Each class which creates hooks has a static Hooks property: an Array of that class's hooks which are running, in the order they were started, as they stood when it was read. A hook which is restarted moves to the end. It covers the hooks started by every real thread. Clr.Hooks mixes instance and static events, which a hook's Target tells apart. InputHook has no such list.
To stop every hook of a class, iterate Hooks:
#Import "Ks" { WinEvent }
for Hook in WinEvent.Hooks
Hook.Stop()
WinEvent, Monitor.OnChange, Clipboard.OnChange, Audio.OnDeviceChange, Clr events, InputHook, Persistent