转自:https://blog.csdn.net/liuyukuan/article/details/138488257
WinEvent简介
WinEvent 可以监视所有窗口或特定窗口的窗口事件。目前支持以下事件:显示、创建、关闭、激活、非激活、移动、开始移动、结束移动、最小化、还原、最大化。有关详细信息,请参见库中函数的注释。
WinEvent.ahk ,以下是2024年5月的版本,该库最新版可在GitHub上获得。
#Requires AutoHotkey v2
/**
* The WinEvent class can monitor window events for all windows or specific windows.
* Currently the following events are supported: `Show`, `Create`, `Close`, `Active`, `NotActive`, `Move`,
* `MoveStart`, `MoveEnd`, `Minimize`, `Restore`, `Maximize`. See comments for the functions for more information.
*
* All the event initiation methods have the same syntax:
* `WinEvent.EventType(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="")`
* where Callback is the function that will be called once the event happened, and Count specifies
* the maximum amount of callbacks.
* The function returns an event hook object that describes the hook (see descriptions down below).
* NOTE: if all WinTitle criteria are left empty then any window will match. To match for Last Found
* Window, use WinExist() as WinTitle.
*
* `Callback(eventObj, hWnd, dwmsEventTime)`
* `eventObj` : the event hook object describing the hook
* `hWnd` : the window handle that triggered the event
* `dwmsEventTime`: the `A_TickCount` for when the event happened
*
* Hook object properties:
* `EventHook.EventType`
* The name of the event type (Show, Close, etc)
* `EventHook.MatchCriteria`
* The window matching criteria in array format `[WinTitle, WinText, ExcludeTitle, ExcludeText]`
* `EventHook.Callback`
* The callback function
* `EventHook.Count`
* The current count of how many times the callback may be called
* `EventHook.Pause(NewState:=1)
* Pauses or unpauses the hook. 1 = pause, 0 = unpause, -1 = toggle
* `EventHook.IsPaused`
* Used to get or set whether the hook is currently active or paused
*
* Hook object methods:
* `EventHook.Stop()`
* Stops the event hook
*
* WinEvent methods (in addition to the event methods):
* `WinEvent.Stop(EventType?, WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")`
* Stops one or all event hooks.
* `WinEvent.Pause(NewState:=1)
* Pauses or unpauses all event hooks. 1 = pause, 0 = unpause, -1 = toggle
* `WinEvent.IsRegistered(EventType, WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
* Checks whether an event with the specified type and criteria is registered.
* `WinEvent.IsEventTypeRegistered(EventType)`
* Checks whether any events for a given event type are registered.
*
* WinEvent properties:
* `WinEvent.IsPaused`
* Can be used to get or set the paused state of all events.
*/
class WinEvent {
; A curated list of event enumerations
static EVENT_OBJECT_CREATE := 0x8000,
EVENT_OBJECT_DESTROY := 0x8001,
EVENT_OBJECT_SHOW := 0x8002,
EVENT_OBJECT_FOCUS := 0x8005,
EVENT_OBJECT_LOCATIONCHANGE := 0x800B,
EVENT_SYSTEM_MINIMIZESTART := 0x0016,
EVENT_SYSTEM_MINIMIZEEND := 0x0017,
EVENT_SYSTEM_MOVESIZESTART := 0x000A,
EVENT_SYSTEM_MOVESIZEEND := 0x000B,
EVENT_SYSTEM_FOREGROUND := 0x0003,
EVENT_OBJECT_NAMECHANGE := 0x800C
/**
* When a window is shown
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Show(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Show", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is created, but not necessarily shown
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Create(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Create", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is destroyed/closed
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Close(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Close", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is activated/focused
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Active(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Active", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is inactivated/unfocused
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static NotActive(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("NotActive", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is moved or resized
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Move(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Move", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is starting to be moved or resized
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static MoveStart(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("MoveStart", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window has been moved or resized
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static MoveEnd(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("MoveEnd", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is minimized
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Minimize(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Minimize", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is restored
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Restore(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Restore", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* When a window is maximized
* @param {(eventObj, hWnd, dwmsEventTime) => Integer} Callback
* - `hWnd` : the window handle that triggered the event
* - `dwmsEventTime`: the `A_TickCount` for when the event happened
* @param {Number} Count Limits the number of times the callback will be called (eg for a one-time event set `Count` to 1).
* @returns {WinEvent}
*/
static Maximize(Callback, WinTitle:="", Count:=-1, WinText:="", ExcludeTitle:="", ExcludeText:="") =>
this("Maximize", Callback, Count, [WinTitle, WinText, ExcludeTitle, ExcludeText])
/**
* Stops one or all event hooks
* @param EventType The name of the event function (eg Close).
* If this isn't specified then all event hooks will be stopped.
*/
static Stop(EventType?, WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="") {
local MatchMap, Hook
if !IsSet(EventType) {
for EventType, MatchMap in this.__RegisteredEvents
for MatchCriteria, Hook in MatchMap
Hook.Stop()
this.__New()
return
}
if !this.__RegisteredEvents.Has(EventType)
return
WinTitle := this.__DeobjectifyWinTitle(WinTitle)
for MatchCriteria, EventObj in this.__RegisteredEvents[EventType].Clone()
if MatchCriteria[1] = WinTitle && MatchCriteria[2] = WinText && MatchCriteria[3] = ExcludeTitle && MatchCriteria[4] = ExcludeText
EventObj.Stop()
}
/**
* Pauses or unpauses all event hooks. This can also be get/set via the `WinEvent.IsPaused` property.
* @param {Integer} NewState 1 = pause, 0 = unpause, -1 = toggle pause state.
*/
static Pause(NewState := 1) => (this.IsPaused := NewState = -1 ? !this.IsPaused : NewState)
/**
* Checks whether an event with the specified type and criteria is registered
* @param EventType The name of the event function (eg Close)
*/
static IsRegistered(EventType, WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="") {
if !this.__RegisteredEvents.Has(EventType)
return 0
WinTitle := this.__DeobjectifyWinTitle(WinTitle)
for MatchCriteria, EventObj in this.__RegisteredEvents[EventType]
if MatchCriteria[1] = WinTitle && MatchCriteria[2] = WinText && MatchCriteria[3] = ExcludeTitle && MatchCriteria[4] = ExcludeText
return 1
return 0
}
/**
* Checks whether any events for a given event type are registered
* @param EventType The name of the event function (eg Close)
*/
static IsEventTypeRegistered(EventType) => this.__RegisteredEvents.Has(EventType)
; Stops the event hook, same as if the object was destroyed.
Stop() => (this.__Delete(), this.MatchCriteria := "", this.Callback := "")
/**
* Pauses or unpauses the event hook. This can also be get/set via the `EventHook.IsPaused` property.
* @param {Integer} NewState 1 = pause, 0 = unpause, -1 = toggle pause state.
*/
Pause(NewState := 1) => (this.IsPaused := NewState = -1 ? !this.IsPaused : NewState)
class Hook {
/**
* Sets a new event hook using SetWinEventHook and returns on object describing the hook.
* When the object is released, the hook is also released.
* @param {(hWinEventHook, event, hwnd, idObject, idChild, idEventThread, dwmsEventTime) => Integer} callbackFunc The function that will be called, which needs to accept 7 arguments.
* @param {Integer} [eventMin] Optional: Specifies the event constant for the lowest event value in the range of events that are handled by the hook function.
* Default is the lowest possible event value.
* - See more about [event constants](https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants)
* - [Msaa Events List](Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd318066(V=Vs.85).Aspx)
* - [System-Level And Object-Level Events](Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373657(V=Vs.85).Aspx)
* - [Console Accessibility](Https://Msdn.Microsoft.Com/En-Us/Library/Ms971319.Aspx)
* @param {Integer} [eventMax] Optional: Specifies the event constant for the highest event value in the range of events that are handled by the hook function.
* If eventMin is omitted then the default is the highest possible event value.
* If eventMin is specified then the default is eventMin.
* @param {Integer|String} [winTitle=0] Optional: WinTitle of a certain window to hook to. Default is system-wide hook.
* @param {Integer} [PID=0] Optional: process ID of the process for which threads to hook to. Default is system-wide hook.
* @param {Integer} [flags=0] Flag values that specify the location of the hook function and of the events to be skipped.
* Default is `WINEVENT_OUTOFCONTEXT` = 0.
* @returns {WinEventHook}
*/
__New(callbackFunc, eventMin?, eventMax?, winTitle := 0, PID := 0, flags := 0) {
if !IsSet(eventMin)
eventMin := 0x00000001, eventMax := IsSet(eventMax) ? eventMax : 0x7fffffff
else if !IsSet(eventMax)
eventMax := eventMin
if !HasMethod(callbackFunc)
throw ValueError("The callbackFunc argument must be a function", -1)
this.callback := callbackFunc, this.winTitle := winTitle, this.flags := flags, this.eventMin := eventMin, this.eventMax := eventMax, this.threadId := 0
if winTitle != 0 {
if !(this.winTitle := WinExist(winTitle))
throw TargetError("Window not found", -1)
this.threadId := DllCall("GetWindowThreadProcessId", "Int", this.winTitle, "UInt*", &PID)
}
this.pCallback := CallbackCreate(callbackFunc, "C", 7)
, this.hHook := DllCall("SetWinEventHook", "UInt", eventMin, "UInt", eventMax, "Ptr", 0, "Ptr", this.pCallback, "UInt", this.PID := PID, "UInt", this.threadId, "UInt", flags)
}
__Delete() {
DllCall("UnhookWinEvent", "Ptr", this.hHook)
, CallbackFree(this.pCallback)
}
}
; ONLY INTERNAL METHODS AHEAD
static __RequiredHooks := Map("Show", [this.EVENT_OBJECT_SHOW], "Create", [this.EVENT_OBJECT_CREATE]
, "Close", [this.EVENT_OBJECT_CREATE, this.EVENT_OBJECT_NAMECHANGE, this.EVENT_OBJECT_DESTROY]
, "Active", [this.EVENT_SYSTEM_FOREGROUND], "NotActive", [this.EVENT_SYSTEM_FOREGROUND]
, "Move", [this.EVENT_OBJECT_LOCATIONCHANGE], "MoveStart", [this.EVENT_SYSTEM_MOVESIZESTART]
, "MoveEnd", [this.EVENT_SYSTEM_MOVESIZEEND], "Minimize", [this.EVENT_SYSTEM_MINIMIZESTART]
, "Maximize", [this.EVENT_OBJECT_LOCATIONCHANGE])
; Internal variables: keep track of registered events (the match criteria) and registered window hooks
static __RegisteredEvents := Map(), __Hooks := Map(), IsPaused := 0
static __New() {
this.Prototype.__WinEvent := this
this.__RegisteredEvents := Map(), this.__RegisteredEvents.CaseSense := 0
this.__Hooks := Map(), this.__Hooks.CaseSense := 0
}
; Extracts hWnd property from an object-type WinTitle
static __DeobjectifyWinTitle(WinTitle) => (IsObject(WinTitle) ? WinTitle.hWnd : WinTitle)
; Activates all necessary window hooks for a given WinEvent type
static __AddRequiredHooks(EventType) {
local _, Hook
for _, Hook in this.__RequiredHooks[EventType]
this.__AddHook(Hook)
}
; Removes (and/or decreases ref count) all necessary window hooks for a given WinEvent type
static __RemoveRequiredHooks(EventType) {
local _, Hook
for _, Hook in this.__RequiredHooks[EventType]
this.__RemoveHook(Hook)
}
; Internal use: activates a new hook if not already active and increases its reference count
static __AddHook(Hook) {
if !this.__Hooks.Has(Hook)
this.__Hooks[Hook] := this.Hook(this.__HandleWinEvent.Bind(this), Hook), this.__Hooks[Hook].RefCount := 0
this.__Hooks[Hook].RefCount++
}
; Internal use: decreases a hooks reference count and removes it if it falls to 0
static __RemoveHook(Hook) {
this.__Hooks[Hook].RefCount--
if !this.__Hooks[Hook].RefCount
this.__Hooks.Delete(Hook)
}
; Internal use: creates a new WinEvent object, which contains info about the registered event
; such as the type, callback function, match criteria etc.
__New(EventType, Callback, Count, MatchCriteria) {
__WinEvent := this.__WinEvent, this.EventType := EventType, this.MatchCriteria := MatchCriteria
, this.Callback := Callback, this.Count := Count, this.IsPaused := 0
MatchCriteria[1] := __WinEvent.__DeobjectifyWinTitle(MatchCriteria[1])
this.MatchCriteria.IsBlank := (MatchCriteria[1] == "" && MatchCriteria[2] == "" && MatchCriteria[3] == "" && MatchCriteria[4] == "")
if InStr(MatchCriteria[1], "ahk_id")
this.MatchCriteria.ahk_id := (RegExMatch(MatchCriteria[1], "ahk_id\s*([^\s]+)", &match) ? (match[1] ? Integer(match[1]) : 0) : 0)
else if IsInteger(MatchCriteria[1])
this.MatchCriteria.ahk_id := MatchCriteria[1]
else
this.MatchCriteria.ahk_id := 0
if EventType = "Close" {
this.__UpdateMatchingWinList()
__WinEvent.__UpdateWinList()
} else if EventType = "NotActive" {
try this.__IsActive := WinActive(MatchCriteria*)
catch
this.__IsActive := 0
}
if !__WinEvent.__RegisteredEvents.Has(EventType)
__WinEvent.__RegisteredEvents[EventType] := Map()
__WinEvent.__RegisteredEvents[EventType][MatchCriteria] := this
__WinEvent.__AddRequiredHooks(EventType)
}
; Internal use: once a WinEvent object is destroyed, deregister the match criteria and remove
; the hook (if no other WinEvent objects depend on it)
__Delete() {
if !this.MatchCriteria
return
this.__WinEvent.__RegisteredEvents[this.EventType].Delete(this.MatchCriteria)
this.__WinEvent.__RemoveRequiredHooks(this.EventType)
}
; Internal use: sets a timer for the callback function (to avoid the thread being Critical
; because the HandleWinEvent thread is Critical). Also keeps track of how many times the
; callback has been called.
__ActivateCallback(args*) {
SetTimer this.Callback.Bind(args*), -1
if --this.Count = 0
this.Stop()
}
; Internal use: handles the event called by SetWinEventHook.
static __HandleWinEvent(hWinEventHook, event, hwnd, idObject, idChild, idEventThread, dwmsEventTime) {
Critical -1
static OBJID_WINDOW := 0, INDEXID_CONTAINER := 0, EVENT_OBJECT_CREATE := 0x8000, EVENT_OBJECT_DESTROY := 0x8001, EVENT_OBJECT_SHOW := 0x8002, EVENT_OBJECT_FOCUS := 0x8005, EVENT_OBJECT_LOCATIONCHANGE := 0x800B, EVENT_SYSTEM_MINIMIZESTART := 0x0016, EVENT_SYSTEM_MINIMIZEEND := 0x0017, EVENT_SYSTEM_MOVESIZESTART := 0x000A, EVENT_SYSTEM_MOVESIZEEND := 0x000B, EVENT_SYSTEM_FOREGROUND := 0x0003, EVENT_OBJECT_NAMECHANGE := 0x800C ; These are duplicated here for performance reasons
if this.IsPaused
return
local PrevDHW := DetectHiddenWindows(1), HookObj, MatchCriteria
idObject := idObject << 32 >> 32, idChild := idChild << 32 >> 32, event &= 0xFFFFFFFF, idEventThread &= 0xFFFFFFFF, dwmsEventTime &= 0xFFFFFFFF ; convert to INT/UINT
if (event = EVENT_OBJECT_DESTROY) {
if !this.WinList.Has(hWnd)
goto Cleanup
for MatchCriteria, HookObj in this.__RegisteredEvents["Close"] {
if !HookObj.IsPaused && HookObj.MatchingWinList.Has(hWnd)
HookObj.__ActivateCallback(HookObj, hWnd, dwmsEventTime)
HookObj.__UpdateMatchingWinList()
}
this.__UpdateWinList()
goto Cleanup
}
if (idObject != OBJID_WINDOW || idChild != INDEXID_CONTAINER || !DllCall("IsTopLevelWindow", "ptr", hWnd))
goto Cleanup
if (event = EVENT_OBJECT_NAMECHANGE || event = EVENT_OBJECT_CREATE) && this.__RegisteredEvents.Has("Close") {
for MatchCriteria, HookObj in this.__RegisteredEvents["Close"]
HookObj.__UpdateMatchingWinList()
if event = EVENT_OBJECT_CREATE
this.__UpdateWinList()
}
if (event = EVENT_OBJECT_LOCATIONCHANGE && this.__RegisteredEvents.Has("Maximize")) { ; Only handles "Maximize"
for MatchCriteria, HookObj in this.__RegisteredEvents["Maximize"] {
if !HookObj.IsPaused && (MatchCriteria.IsBlank || (MatchCriteria.ahk_id ? MatchCriteria.ahk_id = hWnd && WinExist(MatchCriteria*) : WinExist(MatchCriteria[1] " ahk_id " hWnd, MatchCriteria[2], MatchCriteria[3], MatchCriteria[4]))) {
if WinGetMinMax(hWnd) != 1
continue
HookObj.__ActivateCallback(HookObj, hWnd, dwmsEventTime)
}
}
}
if ((event = EVENT_OBJECT_LOCATIONCHANGE && EventName := "Move")
|| (event = EVENT_OBJECT_CREATE && EventName := "Create")
|| (event = EVENT_OBJECT_SHOW && EventName := "Show")
|| (event = EVENT_SYSTEM_MOVESIZESTART && EventName := "MoveStart")
|| (event = EVENT_SYSTEM_MOVESIZEEND && EventName := "MoveEnd")
|| (event = EVENT_SYSTEM_MINIMIZESTART && EventName := "Minimize")
|| (event = EVENT_SYSTEM_MINIMIZEEND && EventName := "Restore")
|| (event = EVENT_SYSTEM_FOREGROUND && EventName := "Active")) && this.__RegisteredEvents.Has(EventName) {
for MatchCriteria, HookObj in this.__RegisteredEvents[EventName] {
if !HookObj.IsPaused && (MatchCriteria.IsBlank || (MatchCriteria.ahk_id ? MatchCriteria.ahk_id = hWnd && WinExist(MatchCriteria*) : WinExist(MatchCriteria[1] " ahk_id " hWnd, MatchCriteria[2], MatchCriteria[3], MatchCriteria[4])))
HookObj.__ActivateCallback(HookObj, hWnd, dwmsEventTime)
}
}
if (event = EVENT_SYSTEM_FOREGROUND && this.__RegisteredEvents.Has("NotActive")) {
for MatchCriteria, HookObj in this.__RegisteredEvents["NotActive"] {
try hWndActive := WinActive(MatchCriteria*)
catch
hWndActive := 0
try if !HookObj.IsPaused && HookObj.__IsActive && !hWndActive {
HookObj.__ActivateCallback(HookObj, HookObj.__IsActive, dwmsEventTime)
HookObj.__IsActive := 0
}
if hWndActive = hWnd
HookObj.__IsActive := hWnd
}
}
Cleanup:
DetectHiddenWindows PrevDHW
Sleep(-1) ; Check the message queue immediately
}
; Internal use: keeps track of all open windows to only handle top-level windows
static __UpdateWinList() {
local WinList := WinGetList(), WinListMap := Map(), hWnd
for hWnd in WinList
WinListMap[hWnd] := 1
this.WinList := WinListMap
}
; Internal use: keeps track of open windows that match the criteria, because matching for name
; class etc wouldn't work after the window is already destroyed.
__UpdateMatchingWinList() {
if !this.MatchCriteria
return
local MatchingWinList := WinGetList(this.MatchCriteria*), MatchingWinListMap := Map(), hWnd
for hWnd in MatchingWinList
MatchingWinListMap[hWnd] := 1
this.MatchingWinList := MatchingWinListMap
}
}
举几个例子
第一个例子 监测WinEvent.Show事件
监测Notepad窗口被创建事件,以Notepad记事本为例,当探测到Notepad窗口创建时,显示一个ToolTip
提示。
说明,需要将本脚本与WinEvent.ahk
放到同一个目录里进行测试。
#Requires AutoHotkey v2
#include WinEvent.ahk
;检测何时创建了Notepad窗口. 按 F1 启动Notepad 来测试.
WinEvent.Show(NotepadCreated, "ahk_class Notepad ahk_exe notepad.exe")
; WinEvent.Show(NotepadCreated, "ahk_exe notepad.exe",1) ;仅仅监控1次。
Persistent()
NotepadCreated(hook, hWnd, dwmsEventTime) {
ToolTip "Notepad 窗口创建于" dwmsEventTime ", 句柄为" hWnd "`n"
SetTimer ToolTip, -3000
}
F1::Run("notepad.exe")
第二个例子 监测WinEvent.Close事件
监测Notepad窗口关闭事件。请注意,如果使用"A"
替换掉WinExist("A")
将检测到任何活动窗口的关闭,而不是Notepad窗口。
第3个参数1表示一旦回调函数被调用一次,钩子就会停止。
#Requires AutoHotkey v2
#include WinEvent.ahk
Run "notepad.exe"
WinWaitActive "ahk_exe notepad.exe"
; Notepad窗口关闭时,会回调ActiveWindowClosed。
WinEvent.Close(ActiveWindowClosed, WinExist("A"), 1)
Persistent()
ActiveWindowClosed(*) {
MsgBox "Notepad 窗口关闭了,点击 确定 按钮 退出"
ExitApp
}
第三个例子 监测WinEvent.Maximize事件
监测窗口最大化事件,这里没指定特定窗口标识,将监视所有窗口的最大化。
#Requires AutoHotkey v2
#include WinEvent.ahk
; 监测所有窗口的最大化事件
WinEvent.Maximize(WindowMaximizedEvent)
Persistent()
WindowMaximizedEvent(hook, hWnd, dwmsEventTime) {
if MsgBox("一个窗口最大化于 " dwmsEventTime ", 句柄" hWnd "`n`n停止监控?",, 0x4) = "Yes"
hook.Stop()
}
F1::Run("notepad.exe")
第四个例子 监测WinEvent.Active事件
测WinEvent.Active事件,实现当窗口激活时显示激活窗口的一些信息。
#Requires AutoHotkey v2
#include WinEvent.ahk
WinEvent.Active(ActiveWindowChanged)
Persistent()
ActiveWindowChanged(hook, hWnd, *) {
ToolTip "激活窗口变了! 新窗口信息为: `n" WinGetInfo(hWnd)
SetTimer ToolTip, -5000
}
/**
* Gets info about a window (title, process name, location etc)
* @param WinTitle Same as AHK WinTitle
* @param {number} Verbose How verbose the output should be (default is 1):
* 0: Returns window title, hWnd, class, process name, PID, process path, screen position, min-max info, styles and ex-styles
* 1: Additionally returns TransColor, transparency level, text (both hidden and not), statusbar text
* 2: Additionally returns ClassNN names for all controls
* @param WinText Same as AHK WinText
* @param ExcludeTitle Same as AHK ExcludeTitle
* @param ExcludeText Same as AHK ExcludeText
* @param {string} Separator Linebreak character(s)
* @returns {string} The info as a string.
* @example MsgBox(WinGetInfo("ahk_exe notepad.exe", 2))
*/
WinGetInfo(WinTitle:="", Verbose := 1, WinText:="", ExcludeTitle:="", ExcludeText:="", Separator := "`n") {
if !(hWnd := WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText))
throw TargetError("Target window not found!", -1)
out := 'Title: '
try out .= '"' WinGetTitle(hWnd) '"' Separator
catch
out .= "#ERROR" Separator
out .= 'ahk_id ' hWnd Separator
out .= 'ahk_class '
try out .= WinGetClass(hWnd) Separator
catch
out .= "#ERROR" Separator
out .= 'ahk_exe '
try out .= WinGetProcessName(hWnd) Separator
catch
out .= "#ERROR" Separator
out .= 'ahk_pid '
try out .= WinGetPID(hWnd) Separator
catch
out .= "#ERROR" Separator
out .= 'ProcessPath: '
try out .= '"' WinGetProcessPath(hWnd) '"' Separator
catch
out .= "#ERROR" Separator
out .= 'Screen position: '
try {
WinGetPos(&X, &Y, &W, &H, hWnd)
out .= "x: " X " y: " Y " w: " W " h: " H Separator
} catch
out .= "#ERROR" Separator
out .= 'MinMax: '
try out .= ((minmax := WinGetMinMax(hWnd)) = 1 ? "maximized" : minmax = -1 ? "minimized" : "normal") Separator
catch
out .= "#ERROR" Separator
static Styles := Map("WS_OVERLAPPED", 0x00000000, "WS_POPUP", 0x80000000, "WS_CHILD", 0x40000000, "WS_MINIMIZE", 0x20000000, "WS_VISIBLE", 0x10000000, "WS_DISABLED", 0x08000000, "WS_CLIPSIBLINGS", 0x04000000, "WS_CLIPCHILDREN", 0x02000000, "WS_MAXIMIZE", 0x01000000, "WS_CAPTION", 0x00C00000, "WS_BORDER", 0x00800000, "WS_DLGFRAME", 0x00400000, "WS_VSCROLL", 0x00200000, "WS_HSCROLL", 0x00100000, "WS_SYSMENU", 0x00080000, "WS_THICKFRAME", 0x00040000, "WS_GROUP", 0x00020000, "WS_TABSTOP", 0x00010000, "WS_MINIMIZEBOX", 0x00020000, "WS_MAXIMIZEBOX", 0x00010000, "WS_TILED", 0x00000000, "WS_ICONIC", 0x20000000, "WS_SIZEBOX", 0x00040000, "WS_OVERLAPPEDWINDOW", 0x00CF0000, "WS_POPUPWINDOW", 0x80880000, "WS_CHILDWINDOW", 0x40000000, "WS_TILEDWINDOW", 0x00CF0000, "WS_ACTIVECAPTION", 0x00000001, "WS_GT", 0x00030000)
, ExStyles := Map("WS_EX_DLGMODALFRAME", 0x00000001, "WS_EX_NOPARENTNOTIFY", 0x00000004, "WS_EX_TOPMOST", 0x00000008, "WS_EX_ACCEPTFILES", 0x00000010, "WS_EX_TRANSPARENT", 0x00000020, "WS_EX_MDICHILD", 0x00000040, "WS_EX_TOOLWINDOW", 0x00000080, "WS_EX_WINDOWEDGE", 0x00000100, "WS_EX_CLIENTEDGE", 0x00000200, "WS_EX_CONTEXTHELP", 0x00000400, "WS_EX_RIGHT", 0x00001000, "WS_EX_LEFT", 0x00000000, "WS_EX_RTLREADING", 0x00002000, "WS_EX_LTRREADING", 0x00000000, "WS_EX_LEFTSCROLLBAR", 0x00004000, "WS_EX_CONTROLPARENT", 0x00010000, "WS_EX_STATICEDGE", 0x00020000, "WS_EX_APPWINDOW", 0x00040000, "WS_EX_OVERLAPPEDWINDOW", 0x00000300, "WS_EX_PALETTEWINDOW", 0x00000188, "WS_EX_LAYERED", 0x00080000, "WS_EX_NOINHERITLAYOUT", 0x00100000, "WS_EX_NOREDIRECTIONBITMAP", 0x00200000, "WS_EX_LAYOUTRTL", 0x00400000, "WS_EX_COMPOSITED", 0x02000000, "WS_EX_NOACTIVATE", 0x08000000)
out .= 'Style: '
try {
out .= (style := WinGetStyle(hWnd)) " ("
for k, v in Styles {
if v && style & v {
out .= k " | "
style &= ~v
}
}
out := RTrim(out, " |")
if style
out .= (SubStr(out, -1, 1) = "(" ? "" : ", ") "Unknown enum: " style
out .= ")" Separator
} catch
out .= "#ERROR" Separator
out .= 'ExStyle: '
try {
out .= (style := WinGetExStyle(hWnd)) " ("
for k, v in ExStyles {
if v && style & v {
out .= k " | "
style &= ~v
}
}
out := RTrim(out, " |")
if style
out .= (SubStr(out, -1, 1) = "(" ? "" : ", ") "Unknown enum: " style
out .= ")" Separator
} catch
out .= "#ERROR" Separator
if Verbose {
out .= 'TransColor: '
try out .= WinGetTransColor(hWnd) Separator
catch
out .= "#ERROR" Separator
out .= 'Transparent: '
try out .= WinGetTransparent(hWnd) Separator
catch
out .= "#ERROR" Separator
PrevDHW := DetectHiddenText(0)
out .= 'Text (DetectHiddenText Off): '
try out .= '"' WinGetText(hWnd) '"' Separator
catch
out .= "#ERROR" Separator
DetectHiddenText(1)
out .= 'Text (DetectHiddenText On): '
try out .= '"' WinGetText(hWnd) '"' Separator
catch
out .= "#ERROR" Separator
DetectHiddenText(PrevDHW)
out .= 'StatusBar Text: '
try out .= '"' StatusBarGetText(1, hWnd) '"' Separator
catch
out .= "#ERROR" Separator
}
if Verbose > 1 {
out .= 'Controls (ClassNN): ' Separator
try {
for ctrl in WinGetControls(hWnd)
out .= '`t' ctrl Separator
} catch
out .= "#ERROR" Separator
}
return SubStr(out, 1, -StrLen(Separator))
}
很棒啊