python-2.7 - 必须在与消息泵相同的线程上注册 Windows 挂钩吗?
问题描述
我正在尝试使用 Windows Hooks 在我的应用程序发送自己的 gui 事件时拦截和阻止击键。
我想出了以下清单:
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()
main 中注释掉的部分来自 pyhook wiki 教程,它工作正常。
然后我尝试将其集成到一个类中,即“WindowsHooksWrapper”类。如果我使用了那个类,它就不起作用,键盘消息会传递到它们的预期目标。
凭直觉,然后我尝试了“WindowsHooksWrapper2”,在那里我将钩子的注册移动到与消息泵相同的线程。现在可以了。
我的预感是否正确,注册要求与泵在同一线程上?如果是这样,为什么?
请注意,我感觉这是 Windows 32 API 的要求,而不是 python 或 pyhook 库本身的要求,因为我是在 C++ 中完成的,并且直接使用“SetWindowsHook”得到了相同的结果。
解决方案
您已经创建了一个线程范围挂钩。
这些钩子事件与特定线程或与调用线程相同的桌面中的所有线程相关联。
pythoncom.PumpWaitingMessages()
在 Python 中和GetMessage
/PeekMessage
在 Win32 中是从“特定线程或与调用线程在同一桌面中的所有线程”获取消息的方法。
要创建一个全局挂钩,为了让所有进程都可以访问您的键盘挂钩,必须将它放在一个 DLL 中,然后将其加载到每个进程的地址空间中。有关如何制作全局键盘挂钩的详细信息,请参阅此答案。
推荐阅读
- amazon-web-services - 在 AWS Athena 中 - 如何以所需格式显示时间戳列?
- svg - 如何在吉他拨片形状的 svg 中使用图像作为背景
- r-lavaan - 比较非嵌套模型之间的标准化因子载荷
- python - 熊猫在最后一个空格后删除数据
- google-apps-script - 如何在谷歌应用脚本中使用 Drive v3?
- c# - 实体框架:连接两个表和 where 子句
- sqlite - 使用包含空格的命名参数准备 SQLite 语句
- reactjs - 从源“http://localhost:3000”访问...的 XMLHttpRequest 已被 CORS 策略阻止
- electron - 将 Blazor 与 Electron.NET 结合使用,当我单击 MenuItem 时,如何在组件内执行代码?
- cadence-workflow - 如何在不等待的情况下获得 Activity 的结果?以事件驱动的方式