在Python中设置WindowsHook(ctypes,WindowsAPI)

2024-09-30 18:35:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试用Python(3.4.3)后台应用程序(windows7/8)全局跟踪鼠标。这涉及到设置一个WindowsHook,它应该向我返回该特定钩子的有效句柄,但是我的句柄总是0。在

使用GetCursorPos只跟踪鼠标位置非常简单(作为替代方案,GetCursorInfo也可以):

from ctypes.wintypes import *
ppoint = ctypes.pointer(POINT())
ctypes.windll.user32.GetCursorPos(ppoint)
print('({}, {})'.format(ppoint[0].x, ppoint[0].y))

还可以方便地只跟踪位置^{},它跟踪最后64个鼠标位置:

^{pr2}$

不过,我想也能跟踪点击,拖动等。 一个好的解决方案似乎是^{}。(可能还有另一种方法有待探索:Raw Input

为了能够使用LealLealMouthPro,文档告诉我们使用^{},它也覆盖在{a5}(C++)tutorials(c ^)中,以及一些有趣的projects(也C ^)。在

<>文件在C++中定义如下:

HHOOK WINAPI SetWindowsHookEx(
  _In_ int       idHook,
  _In_ HOOKPROC  lpfn,
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);

在python中,以下值应该是我的正确值:

  • idHook:^{}
  • hModHINSTANCE(0)(基本上是一个空指针)
  • dwThreadIdctypes.windll.kernel32.GetCurrentThreadId()

对于lpfn,我需要一些实现^{}的回调函数,这里是LLMouseProc

def _LLMouseProc (nCode, wParam, lParam):
    return ctypes.windll.user32.CallNextHookEx(None, nCode, wParam, lParam)
LLMouseProcCB = ctypes.CFUNCTYPE(LRESULT, ctypes.c_int, WPARAM, LPARAM)
LLMouseProc = LLMouseProcCB(_LLMouseProc)

综上所述,我希望这能奏效:

from ctypes.wintypes import *

LONG_PTR = ctypes.c_long
LRESULT = LONG_PTR
WH_MOUSE_LL = 14

def _LLMouseProc(nCode, wParam, lParam):
    print("_LLMouseProc({!s}, {!s}, {!s})".format(nCode, wParam, lParam))
    return ctypes.windll.user32.CallNextHookEx(None, nCode, wParam, lParam)
LLMouseProcCB = ctypes.CFUNCTYPE(LRESULT, ctypes.c_int, WPARAM, LPARAM)
LLMouseProc = LLMouseProcCB(_LLMouseProc)

threadId = ctypes.windll.kernel32.GetCurrentThreadId()

# register callback as hook
print('hook = SetWindowsHookExW({!s}, {!s}, {!s}, {!s})'.format(WH_MOUSE_LL, LLMouseProc,
    HINSTANCE(0), threadId))
hook = ctypes.windll.user32.SetWindowsHookExW(WH_MOUSE_LL, LLMouseProc, 
    HINSTANCE(0), threadId)
print('Hook: {}'.format(hook))

import time
try:
    while True:
        time.sleep(0.2)
except keyboardInterrupt:
    pass

但是输出显示hook == 0

hook = SetWindowsHookExW(14, <CFunctionType object at 0x026183F0>, c_void_p(None), 5700)
Hook: 0

我认为回调函数的最后一个参数,名称lParam与LPARAM(即ctypes.c_long)不太正确,因为我假设实际需要的是指向这个结构的指针:

class MSLLHOOKSTRUCT(ctypes.Structure):
    _fields_ = [
        ("pt", POINT),
        ("mouseData", DWORD),
        ("flags", DWORD),
        ("time", DWORD),
        ("dwExtraInfo", ULONG_PTR)
    ]

但是将签名改为LLMouseProcCB = ctypes.CFUNCTYPE(LRESULT, ctypes.c_int, WPARAM, ctypes.POINTER(MSLLHOOKSTRUCT))并不能解决问题,我仍然有一个0的钩子。在

这是追踪鼠标的正确方法吗?我需要做些什么才能正确注册Windows钩子?在


Tags: informat鼠标hookctypesintprintwindll
1条回答
网友
1楼 · 发布于 2024-09-30 18:35:33

如果您选中GetLastError,您会发现错误是ERROR_GLOBAL_ONLY_HOOK(1429),即WH_MOUSE_LL需要设置一个全局钩子。dwThreadId参数用于设置本地钩子。幸运的是,WH_MOUSE_LL是不寻常的,因为全局钩子回调可以是挂接过程中的任何函数,而不必在DLL中定义,即hMod可以是{}。在

如果需要支持32位Windows,请注意调用约定。通常,{cd8}的回调需要

另一个问题是您的代码缺少消息循环。设置钩子的线程需要运行一个消息循环,以便将消息发送到回调。在下面的示例中,我为这个消息循环使用了一个专用线程。线程设置钩子并进入一个循环,该循环仅在出错时或在发布WM_QUIT消息时中断。当用户输入Ctrl+C时,我调用PostThreadMessageW优雅地退出。在

from ctypes import *
from ctypes.wintypes import *

user32 = WinDLL('user32', use_last_error=True)

HC_ACTION = 0
WH_MOUSE_LL = 14

WM_QUIT        = 0x0012
WM_MOUSEMOVE   = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP   = 0x0202
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP   = 0x0205
WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP   = 0x0208
WM_MOUSEWHEEL  = 0x020A
WM_MOUSEHWHEEL = 0x020E

MSG_TEXT = {WM_MOUSEMOVE:   'WM_MOUSEMOVE',
            WM_LBUTTONDOWN: 'WM_LBUTTONDOWN',
            WM_LBUTTONUP:   'WM_LBUTTONUP',
            WM_RBUTTONDOWN: 'WM_RBUTTONDOWN',
            WM_RBUTTONUP:   'WM_RBUTTONUP',
            WM_MBUTTONDOWN: 'WM_MBUTTONDOWN',
            WM_MBUTTONUP:   'WM_MBUTTONUP',
            WM_MOUSEWHEEL:  'WM_MOUSEWHEEL',
            WM_MOUSEHWHEEL: 'WM_MOUSEHWHEEL'}

ULONG_PTR = WPARAM
LRESULT = LPARAM
LPMSG = POINTER(MSG)

HOOKPROC = WINFUNCTYPE(LRESULT, c_int, WPARAM, LPARAM)
LowLevelMouseProc = HOOKPROC

class MSLLHOOKSTRUCT(Structure):
    _fields_ = (('pt',          POINT),
                ('mouseData',   DWORD),
                ('flags',       DWORD),
                ('time',        DWORD),
                ('dwExtraInfo', ULONG_PTR))

LPMSLLHOOKSTRUCT = POINTER(MSLLHOOKSTRUCT)

def errcheck_bool(result, func, args):
    if not result:
        raise WinError(get_last_error())
    return args

user32.SetWindowsHookExW.errcheck = errcheck_bool
user32.SetWindowsHookExW.restype = HHOOK
user32.SetWindowsHookExW.argtypes = (c_int,     # _In_ idHook
                                     HOOKPROC,  # _In_ lpfn
                                     HINSTANCE, # _In_ hMod
                                     DWORD)     # _In_ dwThreadId

user32.CallNextHookEx.restype = LRESULT
user32.CallNextHookEx.argtypes = (HHOOK,  # _In_opt_ hhk
                                  c_int,  # _In_     nCode
                                  WPARAM, # _In_     wParam
                                  LPARAM) # _In_     lParam

user32.GetMessageW.argtypes = (LPMSG, # _Out_    lpMsg
                               HWND,  # _In_opt_ hWnd
                               UINT,  # _In_     wMsgFilterMin
                               UINT)  # _In_     wMsgFilterMax

user32.TranslateMessage.argtypes = (LPMSG,)
user32.DispatchMessageW.argtypes = (LPMSG,)

@LowLevelMouseProc
def LLMouseProc(nCode, wParam, lParam):
    msg = cast(lParam, LPMSLLHOOKSTRUCT)[0]
    if nCode == HC_ACTION:
        msgid = MSG_TEXT.get(wParam, str(wParam))
        msg = ((msg.pt.x, msg.pt.y),
                msg.mouseData, msg.flags,
                msg.time, msg.dwExtraInfo)
        print('{:15s}: {}'.format(msgid, msg))
    return user32.CallNextHookEx(None, nCode, wParam, lParam)

def mouse_msg_loop():
    hHook = user32.SetWindowsHookExW(WH_MOUSE_LL, LLMouseProc, None, 0)
    msg = MSG()
    while True:
        bRet = user32.GetMessageW(byref(msg), None, 0, 0)
        if not bRet:
            break
        if bRet == -1:
            raise WinError(get_last_error())
        user32.TranslateMessage(byref(msg))
        user32.DispatchMessageW(byref(msg))

if __name__ == '__main__':
    import time
    import threading
    t = threading.Thread(target=mouse_msg_loop)
    t.start()
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            user32.PostThreadMessageW(t.ident, WM_QUIT, 0, 0)
            break

相关问题 更多 >