pyqt全局鼠标事件/钩子

时间:2022-12-15 23:02:08

之前我们用RegisterHotKey实现了全局热键。今天我们来学习一下全局钩子的知识。来结束我这几天的研究。笔者用的是python3.6

首先我们要明白一些关键的部分:
钩子分为线程钩子和系统钩子两种。
线程钩子是局部的,所以qt自带的事件已经可以实现了。我们来学习一下系统钩子的写法,因为系统钩子是全局的。ps.暂且原谅我这样描述。大概意思就是这样的,各位看官老爷们可以去百度详细的讲解。

系统钩子是注册进去以后系统帮忙调用回调事件。但这里注意啦。这个回调事件必须用c写成dll供python调用。这个无解。。具体原因可以查个大概。就是系统需要让所有程序执行这个事件(调用这个dll)。

我们用到的就是这个函数windll.user32.SetWindowsHookExA下面是代码:

from ctypes import  windll

ss = windll.user32.SetWindowsHookExA()

print(ss)

ss是返回的钩子句柄,得到它有很多好处,我们暂且向下看。

SetWindowsHookExA这个函数在user32.dll里面,用dll函数查看器即可查看。
另外它里面还有SetWindowsHookA
SetWindowsHookExW
SetWindowsHookW
下面是这几个的区别。其实区别不大~~
http://bbs.csdn.net/topics/66391
http://blog.csdn.net/vlily/article/details/8129504

当然,必不可少的就是这个函数的参数了,这里我会详细讲解的:
http://baike.sogou.com/v7807464.htm?fromTitle=setwindowshookex

我们从它的第一个参数说起:
int idHook这是一个钩子类型。可以监管系统几乎所有事情,不只是鼠标键盘哦~~

http://blog.sina.com.cn/s/blog_547901e30102v6n0.html
这个是参数的大概意思,下面是参数的数值。因为python没法像c语言一样可以直接调用winuser.h
嘛,所以笔者在这里准备了下面这个。可以在python里面直接填数值哦。是不是很贴心~~
http://blog.csdn.net/acheld/article/details/53386347

HOOKPROC lpfn其实就是一个函数的指针。如果做系统钩子的话这个函数必须写在dll里面。以下是大概的写法。
http://blog.sina.com.cn/s/blog_4513dde60100o6od.html
上面这个介绍了一种用ctypes填这个参数的方法,他是用python里面的函数当做第二个参数。
很遗憾,我上面说的无解,所以必须把执行函数写在dll里面。

http://blog.csdn.net/qingdujun/article/details/25861615
这个是笔者编写dll的方法,其实笔者也不会c语言啦~哈哈哈

#include <windows.h> 

HHOOK g_hMouse = NULL;  
bool cg = true;

//鼠标钩子过程 
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)  
{  
    //CallNextHookEx(g_hMouse, nCode, wParam, lParam);
    return 0;
}  
//安装鼠标钩子过程的函数 
HHOOK SetHook()  
{  
    g_hMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, GetModuleHandle("GHookDll"),0);
    return g_hMouse;
}  
//删除鼠标钩子
bool ShanHook()
{
    cg = UnhookWindowsHookEx(g_hMouse);
    return cg;
}

其实上面的SetHook和ShanHook函数完全不用写啦,在python里面写即可,唯有MouseProc函数必须写在这里面。返回0则放任系统,返回1则丢掉信息。鼠标将卡死。

CallNextHookEx这个函数被我注释掉了,使用它与不使用它没有影响,所以在这里不多说了。
http://baike.sogou.com/v10464092.htm?fromTitle=callnexthookex
如果有好奇的朋友可以在下面留言。

from ctypes import CDLL

zz = CDLL("GHookDll.dll")
ss = zz.SetHook()
dd = zz.ShanHook()

没错。。用ctypes调用dll就是这么简单。。。
如果做到以上步骤基本可以实现全局钩子了,另外推荐多研究一下钩子类型,不同的钩子效果是不同的。
下面我们再来用python试一下调用这个函数。

zz = CDLL("GHookDll.dll")
ss = windll.user32.SetWindowsHookExA(3,zz.MouseProc,windll.kernel32.GetModuleHandleA("GHookDll"), 0)

这样就是在python里面调用这个函数,大家可以和dll里面写的对比一下。
第三个参数的意思就是第二个参数所在的模块句柄。很简单的调用了GetModuleHandleA是不是?~~
这里有个小区别GetModuleHandle与GetModuleHandleA。如果在python里面搜不到的函数。一定要用dll函数查看器查看一下那个函数到底叫什么名字。略坑。。。

如果做系统钩子,第四个函数保持0即可。但俺还是要说两句。
第4个参数是python线程id。但是python的threading#多线程只有名字name,如果id的话需要调用win api里面的函数。返回当前线程的id,ps.注意不是进程id哦。

恕笔者无力,实在是没找到返回线程id的函数了,所以只能姑且填0了。

按理说,python调用的和dll调用的应该是一样的。但是
WH_MOUSE = 7
WH_MOUSE_LL = 14
在python里只有14成功,7会失败。在dll里面两个却都会成功。
大家可以尝试一下其他的类型。可能会有意想不到的收获哦。

大家也可以去研究一下按键精灵的钩子,应该也有很多人在讨论。
下面说一下遇到的问题:
1.无论什么钩子,我弄完以后都会爆卡无比,不知原因。可能是因为win7系统的问题吧。
2.python调用和dll调用显然是不一样的。

from ctypes import CDLL, windll, c_int
import time
import threading#多线程




def kaishigouzi():
    zz = CDLL("GHookDll.dll")
    ss = zz.SetHook()
    print(ss)
    time.sleep(1)

    print("删除钩子")

    zhende = zz.ShanHook()
    print(zhende)

def kaishigouzi_2():
    zz = CDLL("GHookDll.dll")
    SetWindowsHookExA = windll.user32.SetWindowsHookExA(3,zz.MouseProc, windll.kernel32.GetModuleHandleA("GHookDll"), 0)
    print(SetWindowsHookExA)

    time.sleep(10)

    ss = windll.user32.UnhookWindowsHookEx(c_int(SetWindowsHookExA))
    print(ss)


t1 = threading.Thread(target=kaishigouzi_2)
#t1.setDaemon(True)
t1.start()

这个是俺写的python部分。dll部分上面也给出了。需要各位看官们自行编译。

后记:

win32 api是一堆很强大的函数,如果有时间的话还是建议买一本偏向底层的书籍仔细学习一下。会对初学者的你有很大帮助的~~