您的位置:首页 > 编程语言 > Qt开发

pyqt全局鼠标事件/钩子

2017-12-11 22:00 239 查看
之前我们用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


4000
然,必不可少的就是这个函数的参数了,这里我会详细讲解的:

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是一堆很强大的函数,如果有时间的话还是建议买一本偏向底层的书籍仔细学习一下。会对初学者的你有很大帮助的~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python qt 鼠标 pyqt 钩子