更改 window 焦点时 AutoHotKey WinActive returns 错误值
AutoHotKey WinActive returns wrong value when changing window focus
在我的 AutoHotKey 脚本中,我使用 #IfWinActive
来检测 Roblox window 是否处于焦点,然后每当左键单击鼠标时按下数字 1
按钮,例如这个:
#IfWinActive, Roblox
LButton::
MouseClick, Left
SendInput, {1}
return
#IfWinActive
它工作得很好,除了当我从 Roblox window 点击回到另一个 window 时。它仍然在第一次点击时触发此代码,导致它在记事本中输入数字 1
(或我将焦点切换到的任何 window)。
我发现当我点击记事本时,焦点仍然在 Roblox window 上,这就是代码仍然触发的原因。所以我尝试将代码更改为:
#IfWinActive, Roblox
LButton::
Sleep, 100
if WinActive("Roblox")
{
MouseClick, Left
SendInput, {1}
}
return
#IfWinActive
假设在 Sleep
完成时焦点会转移到记事本 window 并且 If WinActive("Roblox")
会 return 错误,但它仍然 returns true 并在记事本中输入 1
。
我也尝试使用 StartTimer
和标签,认为 Sleep
可能不是异步的,但也有同样的问题。
有人知道如何解决这个问题吗?提前致谢!
这种情况下的主要问题是按下 LButton 后立即触发热键并且 Roblox window 仍处于活动状态。
我看到的唯一解决方案是使用 tilde prefix (~) 在释放 LButton 时触发热键,以防止 AHK 阻止 key-down/up 事件:
#IfWinActive, Roblox
~LButton Up:: SendInput, 1
#IfWinActive
我们可以通过多种方式实现这一目标。 TL;DR 解决方案,检查此 post.
的黄色部分
首先,我将解决您代码中的问题:
MouseClick
over Click
的用法。技术上没有错,但据说 Click
在某些情况下更可靠且更易于使用。看起来也更干净了。
- 不需要将
1
包裹在 {}
中,这里对您没有任何帮助。在某些情况下,这样做甚至可能会产生不需要的行为。在发送命令中,{}
用于转义具有特殊含义的键,或定义不能直接输入的键。有关此内容的更多信息,请参见 documentation.
- 你的匹配对象 不好 WinTitle。同样,技术上没有错,但现在你匹配任何以单词 Roblox 开头的 window。不应该太用力一不小心匹配错了window.
一个快速且非常有效的解决方案是匹配您的 Roblox window.
的进程名称
所以 #IfWinActive, ahk_exe Roblox.exe
或在 if 语句中 if (WinActive("ahk_exe Roblox.exe"))
(假设这是进程的名称,我不知道)
对于绝对简单的方法可以与 Roblox window 的 hwnd 相匹配。但是,这可能有点矫枉过正,您也不能 真的 将它与 #IfWinActive
一起使用。不过,我将在下面编写的示例将使用它。
但是,问题 1 和 2 可以通过这种重新映射键的巧妙方法完全避免(重新映射几乎就是您在这里所做的)。
~LButton::1
好的,那为什么有效?
key::key
只是简单地进行基本重新映射的语法,并且使用 ~
我们指定热键在触发时不被消耗。
很酷,但现在进入您遇到的实际问题。
那么睡觉的东西出了什么问题呢?既然您正在使用热键,那么您实际上所做的就是按下热键,等待 100 毫秒,然后检查 Roblox 是否处于活动状态。嗯,是的,它仍将处于活动状态,因为没有采取任何措施将焦点从它移开。
如果您不使用左键单击操作,它会起作用,但是这绝对不是一个好主意。你不想睡在热键语句中。 AHK 没有真正的多线程,除非你为你的热键指定了一个更高的 #MaxThreadsPerHotkey
,否则所有后续的热键按下都会在这 100 毫秒内被完全忽略。
所以是的,通过为该热键指定更多的线程可以 运行 ,它会使这个解决方案起作用,但它仍然是不好的做法。我们可以想出更好的办法。
使用定时器可以避免在热键语句中休眠。听起来你已经尝试过定时器了,但我不能确定它是否正确,因为没有提供代码所以我会检查一下:
#IfWinActive, ahk_exe Roblox.exe
~LButton::SetTimer, OurTimersCallbackLabel, -100 ;-100 specifies that it runs ONCE after 100ms has passed
#IfWinActive
OurTimersCallbackLabel:
if (WinActive("ahk_exe Roblox.exe"))
SendInput, 1
return
And now onto the real solution, to which @user3419297 seems to have beat me to, just as I'm writing this line of text.
使用 LButton 按下的向上事件作为热键。
#IfWinActive, ahk_exe Roblox.exe
~LButton Up::SendInput, 1
#IfWinActive
这样按下事件已经切换了 window 的焦点,我们的热键甚至不会触发。
请注意,不幸的是,在这里我们不能使用我上面描述的 key::key
重映射方式。
奖金:
如果我们的按键的向上事件不受欢迎,或者活动 window 的 window 切换被延迟,可以使用这里的东西。
RobloxHwnd := WinExist("ahk_exe Roblox.exe")
#If, RobloxUnderMouse()
~LButton::1
#If
RobloxUnderMouse()
{
global RobloxHwnd ;specify that we're using the variable defined outside of this function scope
;could've also ran the code to get Roblox's hwnd here every time, but that would be wasteful
MouseGetPos, , , HwndUnderMouse ;we don't need the first two parameters
return RobloxHwnd == HwndUnderMouse ;are they the same hwnd? (return true or false)
}
这里我们首先将 Roblox 的 hwnd 存储到变量 RobloxHwnd
。
请注意,在我们 运行 这个脚本之前,Roblox 需要 运行ning,如果你重新启动 robox,脚本也需要重新启动。
所以添加一些方法来动态更新这个变量的值会很好,也许在一些热键下。
然后通过使用 #If
我们正在评估一个表达式(在我们的例子中,运行 一个函数并评估它的 return 值)每次我们将要尝试触发热键。如果表达式的计算结果为真,我们将触发热键。
#If
实际上 不推荐 的用法,最好尽可能避免使用。但是,在这么小的脚本中你不会遇到任何问题,所以在这里使用 #If
会非常方便。
如果你有一个更大的脚本,其中经常有很多代码 运行ning,你很可能 运行 会遇到问题。
在我的 AutoHotKey 脚本中,我使用 #IfWinActive
来检测 Roblox window 是否处于焦点,然后每当左键单击鼠标时按下数字 1
按钮,例如这个:
#IfWinActive, Roblox
LButton::
MouseClick, Left
SendInput, {1}
return
#IfWinActive
它工作得很好,除了当我从 Roblox window 点击回到另一个 window 时。它仍然在第一次点击时触发此代码,导致它在记事本中输入数字 1
(或我将焦点切换到的任何 window)。
我发现当我点击记事本时,焦点仍然在 Roblox window 上,这就是代码仍然触发的原因。所以我尝试将代码更改为:
#IfWinActive, Roblox
LButton::
Sleep, 100
if WinActive("Roblox")
{
MouseClick, Left
SendInput, {1}
}
return
#IfWinActive
假设在 Sleep
完成时焦点会转移到记事本 window 并且 If WinActive("Roblox")
会 return 错误,但它仍然 returns true 并在记事本中输入 1
。
我也尝试使用 StartTimer
和标签,认为 Sleep
可能不是异步的,但也有同样的问题。
有人知道如何解决这个问题吗?提前致谢!
这种情况下的主要问题是按下 LButton 后立即触发热键并且 Roblox window 仍处于活动状态。
我看到的唯一解决方案是使用 tilde prefix (~) 在释放 LButton 时触发热键,以防止 AHK 阻止 key-down/up 事件:
#IfWinActive, Roblox
~LButton Up:: SendInput, 1
#IfWinActive
我们可以通过多种方式实现这一目标。 TL;DR 解决方案,检查此 post.
的黄色部分
首先,我将解决您代码中的问题:
MouseClick
overClick
的用法。技术上没有错,但据说Click
在某些情况下更可靠且更易于使用。看起来也更干净了。- 不需要将
1
包裹在{}
中,这里对您没有任何帮助。在某些情况下,这样做甚至可能会产生不需要的行为。在发送命令中,{}
用于转义具有特殊含义的键,或定义不能直接输入的键。有关此内容的更多信息,请参见 documentation. - 你的匹配对象 不好 WinTitle。同样,技术上没有错,但现在你匹配任何以单词 Roblox 开头的 window。不应该太用力一不小心匹配错了window.
一个快速且非常有效的解决方案是匹配您的 Roblox window.
的进程名称 所以#IfWinActive, ahk_exe Roblox.exe
或在 if 语句中if (WinActive("ahk_exe Roblox.exe"))
(假设这是进程的名称,我不知道)
对于绝对简单的方法可以与 Roblox window 的 hwnd 相匹配。但是,这可能有点矫枉过正,您也不能 真的 将它与#IfWinActive
一起使用。不过,我将在下面编写的示例将使用它。
但是,问题 1 和 2 可以通过这种重新映射键的巧妙方法完全避免(重新映射几乎就是您在这里所做的)。
~LButton::1
好的,那为什么有效?
key::key
只是简单地进行基本重新映射的语法,并且使用 ~
我们指定热键在触发时不被消耗。
很酷,但现在进入您遇到的实际问题。
那么睡觉的东西出了什么问题呢?既然您正在使用热键,那么您实际上所做的就是按下热键,等待 100 毫秒,然后检查 Roblox 是否处于活动状态。嗯,是的,它仍将处于活动状态,因为没有采取任何措施将焦点从它移开。
如果您不使用左键单击操作,它会起作用,但是这绝对不是一个好主意。你不想睡在热键语句中。 AHK 没有真正的多线程,除非你为你的热键指定了一个更高的 #MaxThreadsPerHotkey
,否则所有后续的热键按下都会在这 100 毫秒内被完全忽略。
所以是的,通过为该热键指定更多的线程可以 运行 ,它会使这个解决方案起作用,但它仍然是不好的做法。我们可以想出更好的办法。
使用定时器可以避免在热键语句中休眠。听起来你已经尝试过定时器了,但我不能确定它是否正确,因为没有提供代码所以我会检查一下:
#IfWinActive, ahk_exe Roblox.exe
~LButton::SetTimer, OurTimersCallbackLabel, -100 ;-100 specifies that it runs ONCE after 100ms has passed
#IfWinActive
OurTimersCallbackLabel:
if (WinActive("ahk_exe Roblox.exe"))
SendInput, 1
return
And now onto the real solution, to which @user3419297 seems to have beat me to, just as I'm writing this line of text.
使用 LButton 按下的向上事件作为热键。
#IfWinActive, ahk_exe Roblox.exe
~LButton Up::SendInput, 1
#IfWinActive
这样按下事件已经切换了 window 的焦点,我们的热键甚至不会触发。
请注意,不幸的是,在这里我们不能使用我上面描述的 key::key
重映射方式。
奖金:
如果我们的按键的向上事件不受欢迎,或者活动 window 的 window 切换被延迟,可以使用这里的东西。
RobloxHwnd := WinExist("ahk_exe Roblox.exe")
#If, RobloxUnderMouse()
~LButton::1
#If
RobloxUnderMouse()
{
global RobloxHwnd ;specify that we're using the variable defined outside of this function scope
;could've also ran the code to get Roblox's hwnd here every time, but that would be wasteful
MouseGetPos, , , HwndUnderMouse ;we don't need the first two parameters
return RobloxHwnd == HwndUnderMouse ;are they the same hwnd? (return true or false)
}
这里我们首先将 Roblox 的 hwnd 存储到变量 RobloxHwnd
。
请注意,在我们 运行 这个脚本之前,Roblox 需要 运行ning,如果你重新启动 robox,脚本也需要重新启动。
所以添加一些方法来动态更新这个变量的值会很好,也许在一些热键下。
然后通过使用 #If
我们正在评估一个表达式(在我们的例子中,运行 一个函数并评估它的 return 值)每次我们将要尝试触发热键。如果表达式的计算结果为真,我们将触发热键。
#If
实际上 不推荐 的用法,最好尽可能避免使用。但是,在这么小的脚本中你不会遇到任何问题,所以在这里使用 #If
会非常方便。
如果你有一个更大的脚本,其中经常有很多代码 运行ning,你很可能 运行 会遇到问题。