3131210 发表于 2024-8-1 10:12:30

【已解决】全局键盘钩子不灵敏的问题,麻烦帮看看

本帖最后由 3131210 于 2024-8-4 22:19 编辑

下面是个例子,使用下面代码,按住键盘的1键,会一直发送3个s,但是按的时间超过几秒后,释放1键后,还会有一段时间继续按,有没有办法解决?

试过换别的发送模式,也不行。

Opt("SendAttachMode", 1) ;模式
Opt("SendKeyDelay", 0)
Opt("SendKeyDownDelay", 0)
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
Global $LinkKeyList = 'None|Shift|Ctrl|Alt|0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12'
Global $KeyCode = '0|16|17|18|48|49|50|51|52|53|54|55|56|57|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|112|113|114|115|116|117|118|119|120|121|122|123'
Global $hHook, $hStub_KeyProc
Global $testkey1 = '1'

$hStub_KeyProc = DllCallbackRegister("_KeyProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL, DllCallbackGetPtr($hStub_KeyProc), _WinAPI_GetModuleHandle(0))

Local $hGUI = GUICreate("Demo", 500, 300)
GUICtrlCreateLabel('ESC 退出' & @CRLF & @CRLF & $testkey1, 10, 60)
GUISetState(@SW_SHOW)

While 1
      Sleep(100)
WEnd

Func _KeyProc($nCode, $wParam, $lParam)
      Local $tKEYHOOKS, $wVKey
      $tKEYHOOKS = DllStructCreate($tagKBDLLHOOKSTRUCT, $lParam)
      If $nCode < 0 Then
                Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
      EndIf
      $wVKey = DllStructGetData($tKEYHOOKS, "vkCode")

      Select
                Case $wVKey = 27
                        Exit

                Case $wVKey = _GetKeyAsc(($testkey1), 2) and _CheckKeyStatus($testkey1) = 1
                        If $wParam = $WM_KEYDOWN Or $wParam = $WM_SYSKEYDOWN Then ;按下
                              ConsoleWrite($testkey1 & " was down." & @CRLF)
                              Send('s')
                              Send('s')
                              Send('s')
                              Return 1
                        ElseIf $wParam = $WM_KEYUP Or $wParam = $WM_SYSKEYUP Then ;弹起
                              ConsoleWrite($testkey1 & " was up." & @CRLF)
                        EndIf
      EndSelect

      Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_KeyProc

Func _CheckKeyStatus($Key)
      Local $aLinkKeyList = StringSplit($LinkKeyList, '|')
      Local $aKeyCode = StringSplit($KeyCode, '|')
      Local $KeyArr = StringSplit($Key, ' + ', 1)

      If UBound($KeyArr) > 2 Then
                For $i = 1 To UBound($KeyArr) - 2
                        If BitAND(0x8000, _WinAPI_GetAsyncKeyState(_GetKeyAsc($KeyArr[$i], 1))) = 0 Then
                              Return 0 ;检测组合键是否全部按下,如果否,则返回0
                        EndIf
                Next
      EndIf
      Return 1
EndFunc   ;==>_CheckKeyStatus

Func _GetKeyAsc($Key, $Mode)
      Local $aLinkKeyList = StringSplit($LinkKeyList, '|')
      Local $aKeyCode = StringSplit($KeyCode, '|')

      Switch $Mode
                Case 1 ;获取单个键值
                        For $i = 1 To UBound($aLinkKeyList) - 1
                              If $aLinkKeyList[$i] = $Key Then
                                        Return $aKeyCode[$i]
                              EndIf
                        Next
                Case 2 ;获取组合键最后一个键的值
                        Local $KeyArr = StringSplit($Key, ' + ', 1)
                        For $i = 1 To UBound($aLinkKeyList) - 1
                              If $aLinkKeyList[$i] = $KeyArr Then
                                        Return $aKeyCode[$i]
                              EndIf
                        Next
      EndSwitch
      Return 0
EndFunc   ;==>_GetKeyAsc

Func _exit()
      _WinAPI_UnhookWindowsHookEx($hHook)
      DllCallbackFree($hStub_KeyProc)
      Exit
EndFunc   ;==>_exit


haijie1223 发表于 2024-8-1 21:08:18

这逻辑看得我头晕眼花,没看明白想干嘛

holley 发表于 2024-8-2 08:16:35

丢AI里面过了一下,不知道有没有实现你要的效果
Opt("SendAttachMode", 1) ;模式
Opt("SendKeyDelay", 0)
Opt("SendKeyDownDelay", 0)
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#include <GUIConstantsEx.au3>

Opt("SendAttachMode", 1)
Opt("SendKeyDelay", 0)
Opt("SendKeyDownDelay", 0)

Global $LinkKeyList = 'None|Shift|Ctrl|Alt|0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12'
Global $KeyCode = '0|16|17|18|48|49|50|51|52|53|54|55|56|57|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|112|113|114|115|116|117|118|119|120|121|122|123'
Global $hHook, $hStub_KeyProc
Global $testkey1 = '1'

Global $g_bKeyPressed = False
Global $g_hTimer = 0
Global $g_iInterval = 100 ; 发送间隔(毫秒)

$hStub_KeyProc = DllCallbackRegister("_KeyProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL, DllCallbackGetPtr($hStub_KeyProc), _WinAPI_GetModuleHandle(0))

Local $hGUI = GUICreate("Demo", 500, 300)
GUICtrlCreateLabel('ESC 退出' & @CRLF & @CRLF & $testkey1, 10, 60)
GUISetState(@SW_SHOW)

While 1
    If $g_bKeyPressed And TimerDiff($g_hTimer) >= $g_iInterval Then
      _SendKeys()
      $g_hTimer = TimerInit()
    EndIf
    Sleep(10)
WEnd

Func _KeyProc($nCode, $wParam, $lParam)
    Local $tKEYHOOKS, $wVKey
    $tKEYHOOKS = DllStructCreate($tagKBDLLHOOKSTRUCT, $lParam)
    If $nCode < 0 Then
      Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
    EndIf
    $wVKey = DllStructGetData($tKEYHOOKS, "vkCode")

    Select
      Case $wVKey = 27
            Exit

      Case $wVKey = _GetKeyAsc(($testkey1), 2) And _CheckKeyStatus($testkey1) = 1
            If $wParam = $WM_KEYDOWN Or $wParam = $WM_SYSKEYDOWN Then ;按下
                If Not $g_bKeyPressed Then
                  $g_bKeyPressed = True
                  ConsoleWrite($testkey1 & " was down." & @CRLF)
                  _SendKeys()
                  $g_hTimer = TimerInit()
                EndIf
            ElseIf $wParam = $WM_KEYUP Or $wParam = $WM_SYSKEYUP Then ;弹起
                $g_bKeyPressed = False
                ConsoleWrite($testkey1 & " was up." & @CRLF)
            EndIf
    EndSelect

    Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_KeyProc

Func _SendKeys()
    Send('s')
    Send('s')
    Send('s')
EndFunc

Func _CheckKeyStatus($Key)
    Local $aLinkKeyList = StringSplit($LinkKeyList, '|')
    Local $aKeyCode = StringSplit($KeyCode, '|')
    Local $KeyArr = StringSplit($Key, ' + ', 1)

    If UBound($KeyArr) > 2 Then
      For $i = 1 To UBound($KeyArr) - 2
            If BitAND(0x8000, _WinAPI_GetAsyncKeyState(_GetKeyAsc($KeyArr[$i], 1))) = 0 Then
                Return 0 ;检测组合键是否全部按下,如果否,则返回0
            EndIf
      Next
    EndIf
    Return 1
EndFunc   ;==>_CheckKeyStatus

Func _GetKeyAsc($Key, $Mode)
    Local $aLinkKeyList = StringSplit($LinkKeyList, '|')
    Local $aKeyCode = StringSplit($KeyCode, '|')

    Switch $Mode
      Case 1 ;获取单个键值
            For $i = 1 To UBound($aLinkKeyList) - 1
                If $aLinkKeyList[$i] = $Key Then
                  Return $aKeyCode[$i]
                EndIf
            Next
      Case 2 ;获取组合键最后一个键的值
            Local $KeyArr = StringSplit($Key, ' + ', 1)
            For $i = 1 To UBound($aLinkKeyList) - 1
                If $aLinkKeyList[$i] = $KeyArr Then
                  Return $aKeyCode[$i]
                EndIf
            Next
    EndSwitch
    Return 0
EndFunc   ;==>_GetKeyAsc

Func _exit()
    _WinAPI_UnhookWindowsHookEx($hHook)
    DllCallbackFree($hStub_KeyProc)
    Exit
EndFunc   ;==>_exit

afan 发表于 2024-8-2 10:15:05

有些代码是不适合在消息中处理的,应该在外部执行。消息应尽快返回,尤其是这类钩子,很容易勾坏系统。

anythinging 发表于 2024-8-2 10:40:00

之前想做个地下城勇士的连击,最终失败

3131210 发表于 2024-8-3 12:16:55

过两天回去试试看
这样说,我放在循环外的话做一个时钟吧
这样也不会阻塞线程了

3131210 发表于 2024-8-4 22:17:53

本帖最后由 3131210 于 2024-8-4 22:19 编辑

确实只要在消息处理中加上判断上次执行的时间,设置时间几十毫秒就不会卡了。而且影响也不大。


Opt("SendAttachMode", 1) ;模式
Opt("SendKeyDelay", 0)
Opt("SendKeyDownDelay", 0)
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
Global $LinkKeyList = 'None|Shift|Ctrl|Alt|0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12'
Global $KeyCode = '0|16|17|18|48|49|50|51|52|53|54|55|56|57|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|112|113|114|115|116|117|118|119|120|121|122|123'
Global $hHook, $hStub_KeyProc
Global $testkey1 = '1'
Global $lastKeyDownTime = 0
Global $keyDownThreshold = 50 ; 阈值时间,单位毫秒

$hStub_KeyProc = DllCallbackRegister("_KeyProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL, DllCallbackGetPtr($hStub_KeyProc), _WinAPI_GetModuleHandle(0))

Local $hGUI = GUICreate("Demo", 500, 300)
GUICtrlCreateLabel('ESC 退出' & @CRLF & @CRLF & $testkey1, 10, 60)
GUISetState(@SW_SHOW)

While 1
        Sleep(1)
WEnd

Func _KeyProc($nCode, $wParam, $lParam)
        Local $tKEYHOOKS, $wVKey
        $tKEYHOOKS = DllStructCreate($tagKBDLLHOOKSTRUCT, $lParam)
        If $nCode < 0 Then
                Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
        EndIf
        $wVKey = DllStructGetData($tKEYHOOKS, "vkCode")

        Select
                Case $wVKey = 27
                        Exit

                Case $wVKey = _GetKeyAsc(($testkey1), 2) And _CheckKeyStatus($testkey1) = 1
                        If $wParam = $WM_KEYDOWN Or $wParam = $WM_SYSKEYDOWN Then ;按下
                                Local $currentTime = TimerInit()
                                If TimerDiff($lastKeyDownTime) >= $keyDownThreshold Then
                                        ConsoleWrite($testkey1 & " was down." & @CRLF)
                                        Send('s')
                                        Send('s')
                                        Send('s')
                                        $lastKeyDownTime = TimerInit() ; 更新最后按键时间
                                EndIf
                                Return 1
                        ElseIf $wParam = $WM_KEYUP Or $wParam = $WM_SYSKEYUP Then ;弹起
                                ConsoleWrite($testkey1 & " was up." & @CRLF)
                        EndIf
        EndSelect

        Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_KeyProc

Func _CheckKeyStatus($Key)
        Local $aLinkKeyList = StringSplit($LinkKeyList, '|')
        Local $aKeyCode = StringSplit($KeyCode, '|')
        Local $KeyArr = StringSplit($Key, ' + ', 1)

        If UBound($KeyArr) > 2 Then
                For $i = 1 To UBound($KeyArr) - 2
                        If BitAND(0x8000, _WinAPI_GetAsyncKeyState(_GetKeyAsc($KeyArr[$i], 1))) = 0 Then
                                Return 0 ;检测组合键是否全部按下,如果否,则返回0
                        EndIf
                Next
        EndIf
        Return 1
EndFunc   ;==>_CheckKeyStatus

Func _GetKeyAsc($Key, $Mode)
        Local $aLinkKeyList = StringSplit($LinkKeyList, '|')
        Local $aKeyCode = StringSplit($KeyCode, '|')

        Switch $Mode
                Case 1 ;获取单个键值
                        For $i = 1 To UBound($aLinkKeyList) - 1
                                If $aLinkKeyList[$i] = $Key Then
                                        Return $aKeyCode[$i]
                                EndIf
                        Next
                Case 2 ;获取组合键最后一个键的值
                        Local $KeyArr = StringSplit($Key, ' + ', 1)
                        For $i = 1 To UBound($aLinkKeyList) - 1
                                If $aLinkKeyList[$i] = $KeyArr Then
                                        Return $aKeyCode[$i]
                                EndIf
                        Next
        EndSwitch
        Return 0
EndFunc   ;==>_GetKeyAsc

Func _exit()
        _WinAPI_UnhookWindowsHookEx($hHook)
        DllCallbackFree($hStub_KeyProc)
        Exit
EndFunc   ;==>_exit


页: [1]
查看完整版本: 【已解决】全局键盘钩子不灵敏的问题,麻烦帮看看