每当目标窗口在AutoHotkey中变为活动状态时如何激活功能 [英] How to activate a function everytime the target window becomes active in 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
对这些消息做出反应,在您的情况下,表示具有测试wParam
是HSHELL_WINDOWACTIVATED
或HSHELL_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_FOREGROUND
和WINEVENT_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屋!