[AHK V2]WinEvent - 简单的检测窗口打开关闭、移动、最大化、最小化等

WinEvent简介

WinEvent 可以监视所有窗口或特定窗口的窗口事件。目前支持以下事件:显示、创建、关闭、激活、非激活、移动、开始移动、结束移动、最小化、还原、最大化。有关详细信息,请参见库中函数的注释。
该库最新版可在Git Hub上获得。
WinEvent.ahk ,以下是2024年5月的版本:

#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))
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/606618.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【解疑】ZIP分卷压缩文件如何设置和取消密码?

压缩大文件&#xff0c;我们可以设置压缩成ZIP分卷文件&#xff0c;这样更利于传输和存储。如果分卷文件比较重要&#xff0c;还可以设置密码保护&#xff0c;那ZIP分卷压缩文件的密码如何设置和取消呢&#xff1f;下面一起来看看吧&#xff01; 设置ZIP分卷密码&#xff1a; …

配电室智能巡检机器人

近年来&#xff0c;生产过程高度自动化&#xff0c;各工矿企业关键场所需定期巡检维护。但目前巡检主要靠人工&#xff0c;既耗时费力效率又低&#xff0c;且受环境等因素影响&#xff0c;巡检难以全面规范&#xff0c;隐患或问题易被忽视。在此情况下&#xff0c;如何利用现有…

Python爬虫基础知识学习(以爬取某二手房数据、某博数据与某红薯(书)评论数据为例)

一、爬虫基础流程 爬虫的过程模块化&#xff0c;基本上可以归纳为以下几个步骤&#xff1a; 1、分析网页URL&#xff1a;打开你想要爬取数据的网站&#xff0c;然后寻找真实的页面数据URL地址&#xff1b; 2、请求网页数据&#xff1a;模拟请求网页数据&#xff0c;这里我们介…

安卓模拟器访问主机局域网

误打误撞能够访问主机局域网了 但是不太懂是因为哪一部分成功的 先记录一下 PC&#xff1a;mac系统 安卓编译器&#xff1a;Android Studio 步骤 只需要在PC上进行设置 1. 在【设置】中&#xff0c;打开已连接的Wi-Fi的【详细信息】 2. TCP/IP --> 配置IPv6&#xff0c;修…

roofline model加速模型部署最后一公里

文章目录 模型部署教程来啦:)什么是Roofline Model&#xff1f;算法模型相关指标计算量计算峰值参数量访存量带宽计算密度kernel size对计算密度的影响output size对计算密度的影响channel size对计算密度的影响group convolution对计算密度的影响tensor reshape对计算密度的影…

网站使用SSL证书有什么好处

SSL证书是一种用于加密在网络上传输的数据以确保安全性和隐私的数字证书。下面我们来谈谈一个网站使用SSL证书后有哪些好处&#xff1a; 首先&#xff0c;使用SSL证书可以保护用户的隐私。在没有SSL证书的情况下&#xff0c;用户的个人信息和敏感数据可能会被黑客窃取或篡改。…

npm安装指定版本,npm删除依赖,卸载依赖

安装指定版本 npm中安装指定的版本号&#xff0c;格式为 ‘包名版本号’ npm install 包名称版本号 --save 例如安装jquery: npm install jquery3.0.0 --save在package.json里面可以看到对应的包&#xff1a; "jquery": "^3.0.0"注意&#xff1a;已有…

基于springboot实现医院药品管理系统项目【项目源码+论文说明】

基于springboot实现医院药品管理系统演示 摘要 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到提升&#xff0c;而读书就…

CentOS 重启网络失败service network restart

命令 service network restart 提示 Job for network.service failed because the control process exited with error code. See “systemctl status network.service” and “journalctl -xe” for details. 原因分析 使用journalctl -xe命令查看日志后的具体错误 -- Un…

基于springboot实现疾病防控综合系统项目【项目源码+论文说明】

基于springboot实现疾病防控综合系统演示 摘要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&…

PDPS15---安装过程---常遇问题---分享

目录 问题1 安装失败 1.1 运行第一步出错 1.2 解决 问题2 路径错误 2.1 错误 2.2 解决 问题3 运行失败 3.1 无法找到路径 3.2 原因分析 3.3 解决 问题4 拒绝访问 4.1 出现提示 4.2 分析 4.3 解决 问题5 许可证过期 5.1 PD找不到许可证 5.2 解决 问题1 安装失败…

hypertherm海宝EDGE控制器显示屏工控机维修

海宝工控机维修V3.0/4.0/5.0&#xff1b;hypertherm数控切割机系统MICRO EDGE系统显示屏维修&#xff1b; 美国hypertherm公司mirco edge数控系统技术标准如下&#xff1a; 1&#xff09; p4处理器 2&#xff09; 512mb内存 3&#xff09; 80g硬盘&#xff0c;1.44m内置软驱…

AXI Block RAM 控制器IP核的用法详解

本文描述了如何使用Xilinx的Vivado Design Suite环境中的工具来定制和生成AXI Block RAM (BRAM) IP 核。Vivado Design Suite是一个强大的FPGA设计和开发环境&#xff0c;它允许用户定制和配置各种IP核以适应他们的特定设计需求。 以下是针对如何定制IP核的步骤的简要概述&…

【FX110】2024外汇市场中交易量最大的货币对是哪个?

作为最大、最流动的金融市场之一&#xff0c;外汇市场每天的交易量高达几万亿美元&#xff0c;涉及到数百种货币。不同货币对的交易活跃程度并不一样&#xff0c;交易者需要根据货币对各自的特点去进行交易。 全年外汇市场中涉及美元的外汇交易超过50%&#xff01; 实际上&…

docker学习笔记(四)制作镜像

目录 第1步&#xff1a;编辑Dockerfile 第2步&#xff1a;编辑requirements.txt文件 第3步&#xff1a;编辑app.py文件&#xff0c;我们的程序文件 第4步&#xff1a;生成镜像文件 第5步&#xff1a;使用镜像&#xff0c;启动容器 第6步&#xff1a; 启动redis容器、将容器…

开启智慧生活,家政服务触手可及——家政小程序全新上线

繁忙生活中的贴心助手 在快节奏的现代生活中&#xff0c;我们时常为家庭琐事所困扰&#xff0c;无暇享受生活的美好。为了帮助您解决这一难题&#xff0c;我们倾力打造了一款家政小程序&#xff0c;让您的生活更加轻松、便捷。 家政小程序&#xff0c;您的生活管家 1. 全方位…

社媒营销中的截流获客是怎么一回事?

如果你要问&#xff0c;现在做社媒营销是通过哪些方式进行引流的&#xff0c;那么必然有一种是截流&#xff0c;顾名思义也就是分取别人的流量&#xff0c;方法其实很简单&#xff0c;主要分为两种&#xff1a;&#xff08;1&#xff09;抓取别人的粉丝出来进行群发私信&#x…

nestjs 全栈进阶--Module和Provider的循环依赖

视频教程 21_nest中的循环依赖_哔哩哔哩_bilibili 1. 循环依赖 当两个类相互依赖时&#xff0c;就会发生循环依赖。比如 A 类需要 B 类&#xff0c;B 类也需要 A 类。Nest中 模块之间和 提供器之间也可能会出现循环依赖。 nest new dependency -p pnpm nest g res aaa --n…

【Java EE】网络原理——UDP

目录 1.应用层 2.传输层 2.1端口号 2.1.1端口号的范围划分 2.1.2一个端口号可以被多个进程绑定吗&#xff1f; 2.1.3一个进程可以绑定多个端口号吗&#xff1f; 3.UDP协议 3.1UDP的格式 3.1.1 UDP的源端口号 3.1.2 UDP的目的端口号 3.1.3 UDP长度 3.1.4UDP校验和 3…

springboot项目中前端页面无法加载怎么办

在springboot前后端分离的项目中&#xff0c;经常会出现前端页面无法加载的情况&#xff08;比如&#xff1a;前端页面为空白页&#xff0c;或者出现404&#xff09;&#xff0c;该怎么办&#xff1f;&#xff1f;&#xff1f; 一个简单有效的方法&#xff1a;&#xff1a; 第…
最新文章