【发布时间】:2015-07-13 08:58:02
【问题描述】:
我正在尝试使用 Python (3.4.3) 后台应用程序(在 Windows 7/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))
也方便只跟踪位置是GetMouseMovePointsEx,它跟踪最后64个鼠标位置:
from ctypes.wintypes import *
# some additional types and structs
ULONG_PTR = ctypes.c_ulong
class MOUSEMOVEPOINT(ctypes.Structure):
_fields_ = [
("x", ctypes.c_int),
("y", ctypes.c_int),
("time", DWORD),
("dwExtraInfo", ULONG_PTR)
]
GMMP_USE_DISPLAY_POINTS = 1
# get initial tracking point
ppoint = ctypes.pointer(POINT())
ctypes.windll.user32.GetCursorPos(ppoint)
point = MOUSEMOVEPOINT(ppoint[0].x,ppoint[0].y)
# track last X points
number_mouse_points = 64
points = (MOUSEMOVEPOINT * number_mouse_points)()
ctypes.windll.user32.GetMouseMovePointsEx(ctypes.sizeof(MOUSEMOVEPOINT),
ctypes.pointer(point), ctypes.pointer(points), number_mouse_points,
GMMP_USE_DISPLAY_POINTS)
# print results
for point in points:
print('({}, {})'.format(point.x, point.y))
但是,我还希望能够跟踪点击、拖动等。
一个好的解决方案似乎是LowLevelMouseProc。 (可能还有另一种方式有待探索:Raw Input)
为了能够使用 LowLevelMouseProc,文档告诉我们使用 SetWindowsHookEx(W/A),various (C++) tutorials (C#) 以及一些有趣的 projects (也是 C#) .
文档在 C++ 中定义如下:
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
以下应该是我在 python 中的正确值:
-
idHook:WH_MOUSE_LL = 14 -
hMod:HINSTANCE(0)(基本上是一个空指针) -
dwThreadId:ctypes.windll.kernel32.GetCurrentThreadId()
对于lpfn,我需要一些回调来实现LowLevelMouseProc,这里是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 中正确注册挂钩?
【问题讨论】: