2012-03-22 30 views
9

Vì vậy, tôi đang cố gắng tìm ra cách đăng ký móc khóa bàn phím chung bằng Python. Từ những gì tôi đã đọc, nó có vẻ là okay để không có gọi lại trong một DLL. Nếu bạn sử dụng WH_KEYBOARD_LL. Tôi không thể xác nhận rằng chắc chắn nhưng tôi thấy nó khuyến khích tôi không nhận được một lỗi 1428 như tôi làm gì nếu tôi cố gắng để móc vào nói WH_CBT.Áp dụng móc khóa bàn phím mức thấp với Python và SetWindowsHookExA

Tôi nhận được một móc xử lý nhưng không có gì xuất hiện khi tôi nhấn các nút trên bàn phím như tôi mong đợi.

Bất kỳ ý tưởng nào về lý do tại sao cuộc gọi lại của tôi không được gọi? Hoặc điều này thậm chí có thể?

Mã liên quan:

import time 
import string 
import ctypes 
import functools 
import atexit 
import pythoncom 
from ctypes import windll 

hookID = 0 

class Keyboard(object): 

    KEY_EVENT_DOWN = 0 
    KEY_EVENT_UP = 2 

    KEY_ENTER = 2 
    KEY_SHIFT = 16 
    KEY_SPACE = 32 

    HOOK_ACTION = 13 
    HOOK_KEYBOARD = 13 
    HOOK_KEYDOWN = 0x100 
    HOOK_KEYUP = 0x101 

    class Hook: 
     '''Holds general hook information''' 
     def __init__(self): 
      self.hook = 0 
      self.struct = None    

    class HookStruct(ctypes.Structure): 
     '''Structure that windows returns for keyboard events''' 
     __fields__ = [ 
      ('keycode', ctypes.c_long), 
      ('scancode', ctypes.c_long), 
      ('flags', ctypes.c_long), 
      ('time', ctypes.c_long), 
      ('info', ctypes.POINTER(ctypes.c_ulong)) 
     ] 

    def ascii_to_keycode(self, char): 
     return windll.user32.VkKeyScanA(ord(char)) 

    def inject_key_down(self, keycode): 
     scancode = windll.user32.MapVirtualKeyA(keycode, 0) 
     windll.user32.keybd_event(keycode, scancode, Keyboard.KEY_EVENT_DOWN, 0) 

    def inject_key_up(self, keycode): 
     scan = windll.user32.MapVirtualKeyA(keycode, 0) 
     windll.user32.keybd_event(keycode, scan, Keyboard.KEY_EVENT_UP, 0) 

    def inject_key_press(self, keycode, pause=0.05): 
     self.inject_key_down(keycode) 
     time.sleep(pause) 
     self.inject_key_up(keycode) 

    def inject_sequence(self, seq, pause=0.05): 
     for key in seq: 
      if key == ' ': 
       self.inject_key_press(Keyboard.KEY_SPACE, pause) 
      elif key == '\n': 
       self.inject_key_press(Keyboard.KEY_ENTER, pause) 
      else: 
       if key in string.ascii_uppercase: 
        self.inject_key_down(Keyboard.KEY_SHIFT) 
        self.inject_key_press(self.ascii_to_keycode(key), pause) 
        self.inject_key_up(Keyboard.KEY_SHIFT) 
       else: 
        self.inject_key_press(self.ascii_to_keycode(key), pause) 

    def _win32_copy_mem(self, dest, src): 
     src = ctypes.c_void_p(src) 
     windll.kernel32.RtlMoveMemory(ctypes.addressof(dest), src, ctypes.sizeof(dest)) 

    def _win32_get_last_error(self): 
     return windll.kernel32.GetLastError() 

    def _win32_get_module(self, mname): 
     return windll.kernel32.GetModuleHandleA(mname) 

    def _win32_call_next_hook(self, id, code, wparam, lparam): 
     return windll.kernel32.CallNextHookEx(id, code, wparam, lparam) 

    def _win32_set_hook(self, id, callback, module, thread): 
     callback_decl = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_long, ctypes.c_long, ctypes.c_long) 
     return windll.user32.SetWindowsHookExA(id, callback_decl(callback), module, thread) 

    def _win32_unhook(self, id): 
     return windll.user32.UnhookWindowsHookEx(id) 

    def keyboard_event(self, data): 
     print data.scancode 
     return False 

    def capture_input(self): 

     self.hook = Keyboard.Hook() 
     self.hook.struct = Keyboard.HookStruct() 

     def low_level_keyboard_proc(code, event_type, kb_data_ptr): 
      # win32 spec says return result of CallNextHookEx if code is less than 0 
      if code < 0: 
       return self._win32_call_next_hook(self.hook.hook, code, event_type, kb_data_ptr) 

      if code == Keyboard.HOOK_ACTION: 
       # copy data from struct into Python structure 
       self._win32_copy_mem(self.hook.struct, kb_data_ptr) 

       # only call other handlers if we return false from our handler - allows to stop processing of keys 
       if self.keyboard_event(self.hook.struct): 
        return self._win32_call_next_hook(self.hook.hook, code, event_type, kb_data_ptr) 

     # register hook 
     try:   
      hookId = self.hook.hook = self._win32_set_hook(Keyboard.HOOK_KEYBOARD, low_level_keyboard_proc, self._win32_get_module(0), 0) 
      if self.hook.hook == 0: 
       print 'Error - ', self._win32_get_last_error() 
      else: 
       print 'Hook ID - ', self.hook.hook 

     except Exception, error: 
      print error 

     # unregister hook if python exits 
     atexit.register(functools.partial(self._win32_unhook, self.hook.hook)) 

    def end_capture(self): 
     if self.hook.hook: 
      return self._win32_unhook(self.hook.hook) 


kb = Keyboard()#kb.inject_sequence('This is a test\nand tHis is line 2') 
kb.capture_input() 
pythoncom.PumpMessages() 
kb.end_capture() 

Trả lời

12

tôi không thể có được lớp học của bạn để làm việc, nhưng tôi tìm thấy một cách tương tự để thực hiện cùng một mục tiêu in this thread.

Dưới đây là mã phù hợp:

from collections import namedtuple 

KeyboardEvent = namedtuple('KeyboardEvent', ['event_type', 'key_code', 
              'scan_code', 'alt_pressed', 
              'time']) 

handlers = [] 

def listen(): 
    """ 
    Calls `handlers` for each keyboard event received. This is a blocking call. 
    """ 
    # Adapted from http://www.hackerthreads.org/Topic-42395 
    from ctypes import windll, CFUNCTYPE, POINTER, c_int, c_void_p, byref 
    import win32con, win32api, win32gui, atexit 

    event_types = {win32con.WM_KEYDOWN: 'key down', 
        win32con.WM_KEYUP: 'key up', 
        0x104: 'key down', # WM_SYSKEYDOWN, used for Alt key. 
        0x105: 'key up', # WM_SYSKEYUP, used for Alt key. 
        } 

    def low_level_handler(nCode, wParam, lParam): 
     """ 
     Processes a low level Windows keyboard event. 
     """ 
     event = KeyboardEvent(event_types[wParam], lParam[0], lParam[1], 
           lParam[2] == 32, lParam[3]) 
     for handler in handlers: 
      handler(event) 

     # Be a good neighbor and call the next hook. 
     return windll.user32.CallNextHookEx(hook_id, nCode, wParam, lParam) 

    # Our low level handler signature. 
    CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p)) 
    # Convert the Python handler into C pointer. 
    pointer = CMPFUNC(low_level_handler) 

    # Hook both key up and key down events for common keys (non-system). 
    hook_id = windll.user32.SetWindowsHookExA(win32con.WH_KEYBOARD_LL, pointer, 
              win32api.GetModuleHandle(None), 0) 

    # Register to remove the hook when the interpreter exits. Unfortunately a 
    # try/finally block doesn't seem to work here. 
    atexit.register(windll.user32.UnhookWindowsHookEx, hook_id) 

    while True: 
     msg = win32gui.GetMessage(None, 0, 0) 
     win32gui.TranslateMessage(byref(msg)) 
     win32gui.DispatchMessage(byref(msg)) 

if __name__ == '__main__': 
    def print_event(e): 
     print(e) 

    handlers.append(print_event) 
    listen() 

Tôi đã thực hiện một thư viện cao cấp để bọc này: keyboard.

+0

Tốt, súc tích, trả lời –

+0

Sau nhiều giờ thử một loạt các cách tiếp cận khác nhau, điều này có vẻ là cách hoàn hảo nếu bạn cần nghe bàn phím ở mức thấp (không có bàn điều khiển tập trung). Cảm ơn! –

+0

Nó hoạt động tốt nhưng tôi nhận được 'key_code' như '240518168740'. Tôi nghĩ rằng nó sẽ trả về các giá trị như [this] (https://msdn.microsoft.com/en-us/library/dd375731%28v=vs.85%29.aspx). Vì vậy, làm cách nào để chuyển đổi thành ký tự/khóa thực tế được nhấn? –

0

Tôi đã không cố gắng này với Python cụ thể, nhưng có, nó phải là có thể cho một bàn phím ở mức độ thấp hoặc móc chuột. Đối với các loại móc khác, các hàm móc phải nằm trong dll.

HOOK_ACTION phải là 0, không phải 13.

4

Lý do mã ban đầu của Tim không hoạt động là do con trỏ hàm ctypes đến low_level_keyboard_proc là rác được thu thập, do đó cuộc gọi lại của anh ta trở nên không hợp lệ và không được gọi. Nó chỉ thất bại âm thầm.

Windows không giữ lại con trỏ Python, vì vậy chúng tôi cần phải giữ riêng biệt tham chiếu đến tham số con trỏ hàm 01pes chính xác callback_decl(callback) ctypes được truyền tới SetWindowsHookEx.

Các vấn đề liên quan