我想用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