如何使windll.user32.SetWinEventHook工作?

问题描述 投票:0回答:1

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

当我启动记事本时,我希望看到“检测到焦点更改事件”打印在控制台上。

当我启动上面的程序时,它会打印“Script is running...”。

当我启动记事本时,上面的程序会静默终止,而不打印焦点更改事件消息或任何其他消息。

python windows events focus hook
1个回答
0
投票

主要问题是使用

WinEventProc(focus_changed)
作为
SetWinEventHook
的参数。这是一个引用计数为零并在调用后立即释放的对象。来自
ctypes
文档:

注意:只要在 C 代码中使用

CFUNCTYPE()
WINFUNCTYPE()
)对象,请确保保留它们的引用。 ctypes 不会,如果您不这样做,它们可能会被垃圾收集,从而在进行回调时使您的程序崩溃。

进行永久引用的一种方法是简单地用原型装饰回调函数。

下面的工作代码。另请注意,在 ctypes 函数上显式声明参数类型是一种很好的做法,以便更好地进行类型和错误检查。

import win32con
import pythoncom
import ctypes as ct
import ctypes.wintypes as w

# WinEventProc and GUITHREADINFO
WinEventProc = ct.WINFUNCTYPE(None, w.HANDLE, w.DWORD, w.HWND, w.LONG, w.LONG, w.DWORD, w.DWORD)

# Decorate the callback for a permanent reference
@WinEventProc
def focus_changed(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
    print("Focus changed event detected")


# Good habit to explicitly declare arguments and result type of all functions
# used by ctypes for better type/error checking.
u32 = ct.WinDLL('user32')
u32.SetWinEventHook.argtypes = w.DWORD, w.DWORD, w.HMODULE, WinEventProc, w.DWORD, w.DWORD, w.DWORD
u32.SetWinEventHook.restype = w.HANDLE
u32.UnhookWinEvent.argtypes = w.HANDLE,
u32.UnhookWinEvent.restype = w.BOOL

# Main code
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
)

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
u32.UnhookWinEvent(hook)
© www.soinside.com 2019 - 2024. All rights reserved.