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

WinEvent 可以监视所有窗口或特定窗口的窗口事件。目前支持以下事件:显示、创建、关闭、激活、非激活、移动、开始移动、结束移动、最小化、还原、最大化。

转自: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))
}

给TA捐赠
共{{data.count}}人
人已捐赠
教程

使用AutoHotkey脚本语言,可以轻松地自动化许多人类处理起来很麻烦的电脑操作

2024-4-24 20:38:22

函数

写了一个修改注册表做自启动的函数,自用分享一下

2024-5-18 9:04:07

2 条回复 A文章作者 M管理员
  1. AHK中文社区
    AHK中文社区给作者打赏了¥2
  2. 立心

    很棒啊

个人中心
购物车
优惠劵
有新私信 私信列表
搜索