Должны ли хуки Windows регистрироваться в той же ветке, что и насос сообщений? - PullRequest
0 голосов
/ 01 мая 2019

Я пытаюсь использовать Windows Hooks для перехвата и блокировки нажатий клавиш, когда мое приложение отправляет свои собственные события графического интерфейса.

Я предложил следующий список:

import pythoncom
import pyHook
import threading
import time


def on_keyboard_event(event):
    print 'MessageName:',event.MessageName
    print 'Message:',event.Message
    print 'Time:',event.Time
    print 'Window:',event.Window
    print 'WindowName:',event.WindowName
    print 'Ascii:', event.Ascii, chr(event.Ascii)
    print 'Key:', event.Key
    print 'KeyID:', event.KeyID
    print 'ScanCode:', event.ScanCode
    print 'Extended:', event.Extended
    print 'Injected:', event.Injected
    print 'Alt', event.Alt
    print 'Transition', event.Transition
    print '---'
    return False


class WindowsHooksWrapper:
    def __init__(self):
        self.started = False
        self.thread = threading.Thread(target=self.thread_proc)
        self.hook_manager = pyHook.HookManager()

    def start(self):
        if self.started:
            self.stop()

        # Register hook
        self.hook_manager.KeyDown = on_keyboard_event
        self.hook_manager.KeyUp = on_keyboard_event
        self.hook_manager.HookKeyboard()

        # Start the windows message pump
        self.started = True
        self.thread.start()

    def stop(self):
        if not self.started:
            return

        self.started = False
        self.thread.join()

        self.hook_manager.UnhookKeyboard()

    def thread_proc(self):
        print "Thread started"
        while self.started:
            pythoncom.PumpWaitingMessages()

        print "Thread exiting..."


class WindowsHooksWrapper2:
    def __init__(self):
        self.started = False
        self.thread = threading.Thread(target=self.thread_proc)

    def start(self):
        if self.started:
            self.stop()

        self.started = True
        self.thread.start()

    def stop(self):
        if not self.started:
            return

        self.started = False
        self.thread.join()

    def thread_proc(self):
        print "Thread started"

        # Evidently, the hook must be registered on the same thread with the windows msg pump or
        #     it will not work and no indication of error is seen
        # Also note that for exception safety, when the hook manager goes out of scope, it
        #     unregisters all outstanding hooks
        hook_manager = pyHook.HookManager()
        hook_manager.KeyDown = on_keyboard_event
        hook_manager.KeyUp = on_keyboard_event
        hook_manager.HookKeyboard()

        while self.started:
            pythoncom.PumpWaitingMessages()

        print "Thread exiting..."
        self.hook_manager.UnhookKeyboard()


def main():
    # hook_manager = pyHook.HookManager()
    # hook_manager.KeyDown = on_keyboard_event
    # hook_manager.KeyUp = on_keyboard_event
    # hook_manager.HookKeyboard()
    # pythoncom.PumpMessages()

    hook_wrapper = WindowsHooksWrapper2()
    hook_wrapper.start()
    time.sleep(30)
    hook_wrapper.stop()


if __name__ == "__main__":
    main()

Закомментированный раздел в основном из учебника Pyhook Wiki, и он отлично работает.

Затем я попытался интегрировать это в класс, который является классом «WindowsHooksWrapper». Если я использовал этот класс, он не работает, и сообщения от клавиатуры проходят по назначению.

По догадке я тогда попробовал «WindowsHooksWrapper2», где я перенес регистрацию хука в ту же ветку с насосом сообщений. Теперь это работает.

Правильно ли мое предчувствие, что регистрация должна быть в той же теме, что и насос? Если так, то почему?

Обратите внимание, что я чувствую, что это требование API Windows 32, а не Python или самой библиотеки Pyhook, потому что я сделал это на C ++ и получил тот же результат, используя SetWindowsHook напрямую.

1 Ответ

1 голос
/ 02 мая 2019

Вы создали зацепку с нитями.

Эти события подключения связаны либо с конкретным потоком, либо со всеми потоками на том же рабочем столе, что и вызывающий поток.

pythoncom.PumpWaitingMessages() в Python и GetMessage / PeekMessage в Win32 - это методы, которые получают сообщение из этого «определенного потока или всех потоков на том же рабочем столе, что и вызывающий поток».

Чтобы создать глобальный хук, чтобы ваш хук клавиатуры был доступен для всех процессов, его нужно поместить в DLL, которая затем будет загружена в адресное пространство каждого процесса.См. Ответ для получения подробной информации о том, как сделать глобальную клавиатуру хуком.

...