每当目标窗口在AutoHotkey中变为活动状态时如何激活功能 [英] How to activate a function everytime the target window becomes active in AutoHotkey

查看:216
本文介绍了每当目标窗口在AutoHotkey中变为活动状态时如何激活功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在每次切换到特定程序时激活一个声音配置文件,并在每次离开它时都更改回默认配置文件. 通过单选按钮可以在GUI中打开此操作.

I would like to activate a sound profile every time I switch to a specific program and change back to the default profile every time I leave it. This action is turned on in a GUI via a radio button.

我创建的解决方法是:

Auto_Ftsps:
    gui, Submit, NoHide
    While (Rad3==1)
    {
        Previous_window:= WinActive("A")
        Sleep,1000
        Current_window:= WinActive("A")
        If (Previous_window =Current_window)
            {}
        Else If (Previous_window !=Current_window) 
        {
            If(WinActive("Fortnite"))
                Run_Peace_Profile("Ftsps")
            Else 
                Run_Peace_Profile("Graphic EQ")
        }
        Sleep,2000
    }
    return

是否有更好的方法可以做到这一点?我看过论坛和教程都没有成功.

Is there a better way to do this? I looked on forums and tutorials with no success.

推荐答案

OnWin.ahk与您的方法有点类似;它使用 SetTimer 定期检查您向其注册的事件,因此与您的方法在AHK线程方面是异步的.不要为此引用我,但我认为内部 WinWaitActive 也是相似的

OnWin.ahk is somewhat similar to your method; it uses SetTimer to periodically check for the events you register with it, so unlike your method it's asynchronous in terms of AHK threads. Don't quote me on this, but I think internally WinWaitActive is similar as well.

但是,还有另一种方法不涉及自己定期检查活动窗口,而是允许我们对Windows的活动窗口更改"事件作出反应-外壳钩子.通常 SetWindowsHookEx WH_SHELL可以用于此目的,但我认为甚至不能单独将其与AHK一起使用(您必须制作一个DLL),并且要使所有内容正确都有些复杂.幸运的是,有 RegisterShellHookWindow ,这使我们可以将外壳事件作为Windows消息接收,而不是将DLL注入其他线程.然后,我们可以使用AHK的 OnMessage 对这些消息做出反应,在您的情况下,表示具有测试wParamHSHELL_WINDOWACTIVATEDHSHELL_RUDEAPPACTIVATED(即,设置了位3)并相应更改声音配置文件的功能.至于打开/关闭此功能,我们可以使单选按钮的g标签包含用于控制是否要通过(De)RegisterShellHookWindow接收外壳消息的逻辑.

There is however another way that doesn't involve periodically checking the active window ourselves and instead allows us to react to Windows' "active window change" events - a shell hook. Typically SetWindowsHookEx with WH_SHELL would be used for this, but I don't think it's even possible to use it with AHK alone (you have to make a DLL), and it's kind of complicated to get everything right. Luckily there's RegisterShellHookWindow, which allows us to receive shell events as Windows messages rather than injecting a DLL into other threads. We can then use AHK's OnMessage to react to these messages, which, in your case, means having a function that tests for to wParam being HSHELL_WINDOWACTIVATED or HSHELL_RUDEAPPACTIVATED (i.e., bit 3 is set) and changes the sound profile accordingly. As for turning this functionality on/off, we can have the radio buttons' g-label contain the logic for controlling whether we want to receive the shell messages via (De)RegisterShellHookWindow.

#SingleInstance Force

Gui +AlwaysOnTop +HwndhWnd
Gui Add, Text,, Automatic sound profile change
Gui Add, Radio, gHookRadioHandler Checked, On
Gui Add, Radio, gHookRadioHandler X+, Off
Gui Font,, Consolas
Gui Add, Edit, HwndhLog xm w800 r30 ReadOnly -Wrap -WantReturn

ftspsActive := false

; Get the dynamic identifier for shell messages and assign our callback to handle these messages
SHELL_MSG := DllCall("RegisterWindowMessage", "Str", "SHELLHOOK", "UInt")
OnMessage(SHELL_MSG, Func("ShellCallback"))

if (!SetHook(true)) {
    GuiControl,, Off, 1
}

Gui Show


GuiClose() {
    ExitApp
}

; Dummy implementation that logs the changes to an edit control for demonstration purposes
Run_Peace_Profile(profile) {
    Println("Switched to " profile)
}

; Sets whether the shell hook is registered
SetHook(state) {
    global hWnd
    static shellHookInstalled := false
    if (!shellHookInstalled and state) {
        if (!DllCall("RegisterShellHookWindow", "Ptr", hWnd)) {
            Println("Failed to register shell hook")
            return false
        }
        Println("Registered shell hook")
        shellHookInstalled := true
    }
    else if (shellHookInstalled and !state) {
        if (!DllCall("DeregisterShellHookWindow", "Ptr", hWnd)) {
            Println("Failed to deregister shell hook")
            return false
        }
        Println("Deregistered shell hook")
        shellHookInstalled := false
    }

    return true
}

; Radio button handler that controls registration of the sound profile hook
HookRadioHandler() {
    state := A_GuiControl == "On"
    if (!SetHook(state)) {
        GuiControl,, % (state ? "Off" : "On"), 1
    }
}

; Shell messages callback
ShellCallback(wParam, lParam) {
    ; HSHELL_WINDOWACTIVATED = 4, HSHELL_RUDEAPPACTIVATED = 0x8004
    if (wParam & 4) {
        ; lParam = hWnd of activated window
        global ftspsActive
        WinGet fnHWnd, ID, Fortnite

        WinGetTitle t, ahk_id %lParam%
        Println("active window: " t)

        if (!ftspsActive and fnHWnd = lParam) {
            Run_Peace_Profile("Ftsps")
            ftspsActive := true
        }
        else if (ftspsActive and fnHWnd != lParam) {
            Run_Peace_Profile("Graphic EQ")
            ftspsActive := false
        }
    }
}

; Prints a line to the logging edit box
Println(s) {
    global hLog
    static MAX_LINES := 1000, LINE_ADJUST := 200, nLines := 0
    ; EM_SETSEL = 0xB1, EM_REPLACESEL = 0xC2, EM_LINEINDEX = 0xBB
    if (nLines = MAX_LINES) {
        ; Delete the oldest LINE_ADJUST lines
        SendMessage 0xBB, LINE_ADJUST,,, ahk_id %hLog%
        SendMessage 0xB1, 0, ErrorLevel,, ahk_id %hLog%
        SendMessage 0xC2, 0, 0,, ahk_id %hLog%
        nLines -= LINE_ADJUST
    }
    ++nLines
    ; Move to the end by selecting all and deselecting
    SendMessage 0xB1, 0, -1,, ahk_id %hLog%
    SendMessage 0xB1, -1, -1,, ahk_id %hLog%
    ; Add the line
    str := "[" A_Hour ":" A_Min "] " s "`r`n"
    SendMessage 0xC2, 0, &str,, ahk_id %hLog%
}

请注意,我以编辑控件的形式添加了一些反馈消息,只是为了使此脚本可以作为小型独立演示.

Note that I added some feedback messages in the form of an edit control just so this script can serve as a small stand-alone demonstration.

此方法的可能缺点来自RegisterShellHookWindow文档的顶部:

A possible drawback of this approach comes right from the top of the RegisterShellHookWindow documentation:

此功能不适用于一般用途.可能会在Windows的后续版本中对其进行更改或不可用.

This function is not intended for general use. It may be altered or unavailable in subsequent versions of Windows.

此外,我不知道什么是粗鲁的应用程序"或为什么它们具有自己的常量. 此问题表示,该问题与屏幕应用程序,但是询问者和我似乎都收到了每个程序的HSHELL_RUDEAPPACTIVATED.

Additionally, I have no idea what a "rude app" is or why they have their own constant. This question says it has to do with full-screen applications, but the asker and I receive HSHELL_RUDEAPPACTIVATED for seemingly every program.

作为替代方案,还有 SetWinEventHook ,可以使用EVENT_SYSTEM_FOREGROUNDWINEVENT_OUTOFCONTEXT进行调用,以安装来自AHK的回调,每次前景窗口更改时都会调用该回调.请注意,与RegisterShellHookWindow方法不同,这将被称为进入子窗口的子窗口.

As an alternative, there's also SetWinEventHook, which can be called with EVENT_SYSTEM_FOREGROUND and WINEVENT_OUTOFCONTEXT to install a callback from AHK which is called every time the foreground window changes. Note that this will be called for child windows coming to the foreground, unlike the RegisterShellHookWindow method.

#SingleInstance Force

Gui +AlwaysOnTop
Gui Add, Text,, Automatic sound profile change
Gui Add, Radio, gHookRadioHandler Checked, On
Gui Add, Radio, gHookRadioHandler X+, Off
Gui Font,, Consolas
Gui Add, Edit, HwndhLog xm w800 r30 ReadOnly -Wrap -WantReturn

ftspsActive := false

fcAddr := RegisterCallback(Func("FgCallback"))

if (!SetHook(true)) {
    GuiControl,, Off, 1
}

Gui Show


GuiClose() {
    ExitApp
}

; Dummy implementation that logs the changes to an edit control for demonstration purposes
Run_Peace_Profile(profile) {
    Println("Switched to " profile)
}

; Sets whether the foreground hook is installed
SetHook(state) {
    global fcAddr
    static hook, fgHookInstalled := false

    if (!fgHookInstalled and state) {
        ; EVENT_SYSTEM_FOREGROUND = 3, WINEVENT_OUTOFCONTEXT = 0
        hook := DllCall("SetWinEventHook", "UInt", 3, "UInt", 3, "Ptr", 0, "Ptr", fcAddr, "Int", 0, "Int", 0, "UInt", 0, "Ptr")
        if (!hook) {
            Println("Failed to set foreground hook")
            return false
        }
        Println("Set foreground hook")
        fgHookInstalled := true
    }
    else if (fgHookInstalled and !state) {
        if (!DllCall("UnhookWinEvent", "Ptr", hook)) {
            Println("Failed to unset foreground hook")
            return false
        }
        Println("Unset foreground hook")
        fgHookInstalled := false
    }

    return true
}

; Radio button handler that controls installation of the sound profile hook
HookRadioHandler() {
    state := A_GuiControl == "On"
    if (!SetHook(state)) {
        GuiControl,, % (state ? "Off" : "On"), 1
    }
}

; Foreground window change callback
FgCallback(hWinEventHook, event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime) {
    global ftspsActive
    WinGet fnHWnd, ID, Fortnite

    WinGetTitle t, ahk_id %hWnd%
    Println("fg window: " t)

    if (!ftspsActive and fnHWnd = hWnd) {
        Run_Peace_Profile("Ftsps")
        ftspsActive := true
    }
    else if (ftspsActive and fnHWnd != hWnd) {
        Run_Peace_Profile("Graphic EQ")
        ftspsActive := false
    }
}

; Prints a line to the logging edit box
Println(s) {
    global hLog
    static MAX_LINES := 1000, LINE_ADJUST := 200, nLines := 0
    ; EM_SETSEL = 0xB1, EM_REPLACESEL = 0xC2, EM_LINEINDEX = 0xBB
    if (nLines = MAX_LINES) {
        ; Delete the oldest LINE_ADJUST lines
        SendMessage 0xBB, LINE_ADJUST,,, ahk_id %hLog%
        SendMessage 0xB1, 0, ErrorLevel,, ahk_id %hLog%
        SendMessage 0xC2, 0, 0,, ahk_id %hLog%
        nLines -= LINE_ADJUST
    }
    ++nLines
    ; Move to the end by selecting all and deselecting
    SendMessage 0xB1, 0, -1,, ahk_id %hLog%
    SendMessage 0xB1, -1, -1,, ahk_id %hLog%
    ; Add the line
    str := "[" A_Hour ":" A_Min "] " s "`r`n"
    SendMessage 0xC2, 0, &str,, ahk_id %hLog%
}

这篇关于每当目标窗口在AutoHotkey中变为活动状态时如何激活功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆