windows 如何使windll.user32.SetWinEventHook工作?

nwlls2ji  于 6个月前  发布在  Windows
关注(0)|答案(1)|浏览(48)

我尝试在windows11,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)

字符串
当我启动记事本时,我希望看到控制台上打印的“检测到焦点更改事件”。
当我启动上面的程序时,它会打印“脚本正在运行..."。
当我启动记事本时,上面的程序无声地终止,没有打印焦点改变事件消息或任何其他消息。

snvhrwxg

snvhrwxg1#

主要问题是使用WinEventProc(focus_changed)作为SetWinEventHook的参数。这是一个引用计数为零的对象,并且在调用后立即被释放。来自ctypes文档:
注意事项:确保只要在C代码中使用CFUNCTYPE()(* 和WINFUNCTYPE()*)对象,就保持对它们的引用。ctypes不这样做,如果不这样做,它们可能会被垃圾收集,在回调时使程序崩溃。
创建永久引用的一种方法是简单地用原型装饰回调函数。
下面的工作代码。还请注意,在ctypes函数上显式声明参数类型是一个很好的做法,可以更好地进行类型和错误检查。例如,ctypes假定非指针参数和所有返回值都是32位整数。句柄是整数,但都是64位的。如果句柄足够大,则值将被截断,除非参数或返回值被声明为句柄。

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)

字符串

相关问题