我想用Python(3.4.3)后台应用程序(在Windows 7/8中)全局跟踪鼠标。这包括建立一个WindowsHook应该还给我一个有效的句柄具体钩 - 但我的手柄始终为0在Python中设置WindowsHook(ctypes,Windows API)
只跟踪鼠标的位置是很容易与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挂钩?
我什至不能告诉我从这个答案中学到了多少。很多关于WinAPI的兴趣,我不得不阅读python装饰器。我不完全明白的是,为什么WH_MOUSE_LL的特殊之处在于你不需要将它订阅到DLL--我从https://msdn.microsoft.com/en-us/library/windows /desktop/ms644990(v=vs.85).aspx,它可以与所有人挂钩到当前线程或DLL。在我看来,唯一的区别是一些钩子只能在全球范围内根据他们得到的事件进行工作。 –
['LowLevelMouseProc'](https://msdn.microsoft.com/en-us/library/ms644986)文档指出钩子是在“安装它的线程的上下文中”通过“发送消息到线程“。在其他情况下,如果没有使用或不能使用线程间消息传递,则具有挂钩过程的DLL需要事先加载(除非无法加载,例如32位DLL可以加载不会在64位进程中加载;这是如何工作的在SetWindowsHookEx'文档中讨论)。 – eryksun