每次目标窗口在AutoHotkey中变为活动状态时如何激活功能

时间:2018-04-02 11:44:01

标签: autohotkey

我希望每次切换到特定程序时激活声音配置文件,并在每次离开时更改回默认配置文件。 通过单选按钮在GUI中打开此操作。

我创建的解决方法是:

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

有更好的方法吗?我查看论坛和教程没有成功。

1 个答案:

答案 0 :(得分:2)

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

然而,有另一种方法不涉及我们自己定期检查活动窗口,而是允许我们对Windows的“活动窗口更改”事件做出反应 - 一个shell挂钩。通常SetWindowsHookEx WH_SHELL将用于此,但我认为甚至不可能单独使用AHK(你必须制作一个DLL),并且它有点复杂以获得一切对。幸运的是有RegisterShellHookWindow,它允许我们接收shell事件作为Windows消息,而不是将DLL注入其他线程。然后我们可以使用AHK的OnMessage来对这些消息做出反应,在您的情况下,这意味着有一个函数可以测试wParamHSHELL_WINDOWACTIVATEDHSHELL_RUDEAPPACTIVATED(即,位3设置)并相应地改变声音配置文件。至于打开/关闭此功能,我们可以让单选按钮的g-label包含用于控制我们是否希望通过(De)RegisterShellHookWindow接收shell消息的逻辑。

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

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

这种方法的一个可能的缺点来自RegisterShellHookWindow文档的顶部:

  

此功能不适用于一般用途。在后续版本的Windows中,它可能会被更改或不可用。

此外,我不知道什么是“粗鲁的应用程序”或为什么他们有自己的常量。 This question表示它与全屏应用程序有关,但问及我看似每个程序HSHELL_RUDEAPPACTIVATED

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

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