提问人:David Hite 提问时间:10/9/2023 最后编辑:Mark TolonenDavid Hite 更新时间:10/29/2023 访问量:89
如何使windll.user32.SetWinEventHook工作?
How do I make windll.user32.SetWinEventHook work?
问:
我正在尝试在 windows 11 python 3.11 中使用以下程序挂钩焦点更改事件。
import win32con
import pythoncom
from ctypes import wintypes, windll, WINFUNCTYPE
# WinEventProc and GUITHREADINFO
WinEventProc = WINFUNCTYPE(
None,
wintypes.HANDLE,
wintypes.DWORD,
wintypes.HWND,
wintypes.LONG,
wintypes.LONG,
wintypes.DWORD,
wintypes.DWORD
)
# focus_changed function
def focus_changed(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
print("Focus changed event detected")
# Main code
hook = windll.user32.SetWinEventHook(
win32con.EVENT_SYSTEM_FOREGROUND,
win32con.EVENT_SYSTEM_FOREGROUND,
0,
WinEventProc(focus_changed),
0,
0,
win32con.WINEVENT_OUTOFCONTEXT
)
if not hook:
print(f"SetWinEventHook failed")
print("Script is running...")
while True:
try:
pythoncom.PumpWaitingMessages()
except KeyboardInterrupt:
print("Exiting...")
break
# Cleanup: Unhook events and release resources
windll.user32.UnhookWinEvent(hook)
当我启动记事本时,我希望看到控制台上打印“检测到焦点更改事件”。
当我启动上面的程序时,它会打印“脚本正在运行...”。
当我启动记事本时,上面的程序会静默终止,而不会打印焦点更改事件消息或任何其他消息。
答:
0赞
Mark Tolonen
10/29/2023
#1
主要问题是用作 的参数。这是一个对象,其引用计数为零,并在调用后立即释放。来自 ctypes
文档:WinEventProc(focus_changed)
SetWinEventHook
注意:请确保保留对(和
WINFUNCTYPE())
对象的引用,只要它们是在 C 代码中使用的。ctypes 不会,如果你不这样做,它们可能会被垃圾回收,在进行回调时导致程序崩溃。CFUNCTYPE()
进行永久引用的一种方法是简单地用原型装饰回调函数。
工作代码如下。另请注意,最好在函数上显式声明参数类型,以便更好地进行类型和错误检查。例如,假定非指针参数和所有返回值为 32 位整数。句柄是整数,但为 64 位。如果句柄足够大,则该值将被截断,除非将参数或返回值声明为句柄。ctypes
ctypes
import win32con
import pythoncom
import ctypes as ct
import ctypes.wintypes as w
WinEventProc = ct.WINFUNCTYPE(None, w.HANDLE, w.DWORD, w.HWND, w.LONG, w.LONG, w.DWORD, w.DWORD)
@WinEventProc # Decorate the callback for a permanent reference
def focus_changed(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
print('Focus changed event detected')
# Failure check helper for ctypes functinos
def fail_check(result, func, args):
if not result:
raise ct.WinError(ct.get_last_error())
return result
# Good habit to explicitly declare arguments and result type of all functions
# used by ctypes for better type/error checking.
# "errcheck" suport will throw an exception with Win32 failure info if the function fails.
u32 = ct.WinDLL('user32', use_last_error=True)
u32.SetWinEventHook.argtypes = w.DWORD, w.DWORD, w.HMODULE, WinEventProc, w.DWORD, w.DWORD, w.DWORD
u32.SetWinEventHook.restype = w.HANDLE
u32.SetWinEventHook.errcheck = fail_check
u32.UnhookWinEvent.argtypes = w.HANDLE,
u32.UnhookWinEvent.restype = w.BOOL
u32.UnhookWinEvent.errcheck = fail_check
hook = u32.SetWinEventHook(
win32con.EVENT_SYSTEM_FOREGROUND, win32con.EVENT_SYSTEM_FOREGROUND, 0,
focus_changed, # changed from an object that immediately goes out of scope after hook call
0, 0, win32con.WINEVENT_OUTOFCONTEXT)
print('Script is running...')
try:
while True:
pythoncom.PumpWaitingMessages()
except KeyboardInterrupt:
print('Exiting...')
u32.UnhookWinEvent(hook)
评论