当前位置:文档之家› VB 钩子详解

VB 钩子详解

Windows钩子函数的概念和实现方法首先我们必须大致了解Windows的基本运作机理,Windows作为一个多任务操作系统,它是分有层次概念的,运行在最底下的称为Ring 0层,在这一层里基本上都是一些硬件驱动程序和Windows的总内核,一般的应用程序极少极少运行在这层,当然也有例外,例如调试软件SoftICE(不过基本上这个软件的作用是Crack软件而不是调试)、还原精灵还有分区魔法大师,就是运行在Ring 0层的,另外就是著名的CIH病毒。

运行在Ring 0级的程序能够对所有硬件进行直接地址级访问,所受到的限制也最小。

消息(Message)传递是Windows独有的一种机制,因为Windows规定运行在Ring 0以上的程序是没有权利知道究竟硬件发生了怎样的中断变化的,Windows统一将这些中断变化封装成一系列的消息(黑箱作业,也就是常说的Black Box),比如鼠标移动,系统产生一个OnMouseMove消息(但这条消息从何而来,相关的硬件中断向量是什么,程序无从得知),OnMouseMove这条消息最后送达每一个窗口程序以供处理。

在更高层次的地方,比如说控件级,所有的消息还被封装成一系列“事件”,比如TextBox控件有KeyPress事件,实际上,这些事件都是林林种种的消息映射。

事件的概念使得程序员能够更加傻瓜化地进行编程,但是从另一个角度来说,这种黑箱作业也使得程序员过分依赖系统的安排,限制了程序员的思维,举个例子,Windows为按钮控件封装了大部分常用的属性和事件,完成一般的常规妈作是没有问题的,但是很遗憾,或许是Windows的疏忽,按钮控件的字体颜色永远默认是黑色,而且Windows没有为此提供一个专门的接口来修改,碰到这种情况,程序员就会非常头疼。

钩子函数(Hook Function),就像一把钩子,它的作用是将消息在抵达窗口程序之前先钩到一个地方以便程序员进行分析,这个地方称为挂接函数链,消息在这里先被一系列的函数处理然后由程序员决定是否交还给Windows系统,在这里,你可以“吞噬”(Lickup)一些你不希望发生的消息,比如说你吞掉所有的键盘消息而不交还给系统,那么键盘将会失灵。

当然,经过了这道周折,系统效率将会有极其微小的降低,但是,由于Windows规定所有不运行在Ring 0层的程序都不能直接访问硬件中断,因此作为一种中断驱动程序的补充,钩子函数在很多场合下是非常有用的,有时候甚至是唯一的方法。

下面就以键盘拦截为例讲述钩子函数的使用方法:首先定义以下API:Public Declare Function SetWindowsHookEx Lib "user32" _Alias "SetWindowsHookExA" (ByVal idHook As Long, _ByVal lpfn As Long, _ByVal hmod As Long, _ByVal dwThreadId As Long) As LongSetWindowsHookEx,这个函数是一切钩子函数的根本,其作用是通知Windows进行钩子妈作并定义钩子函数。

参数1,idHook为定义需要进行的拦截类型,是键盘拦截?鼠标拦截?还是别的。

如 WH_KEYBOARD捕捉键盘消息,而WH_MOUSE捕捉鼠标消息。

参数2,lpfn为该挂接函数链的首地址指针,因为VB是没有指针这种数据类型的所以用Long 代替。

lpfn为钩子函数,在 VB中可以使用 AddressOf 获得钩子函数的地址。

这个函数因为钩子类型不同而有所不同。

如键盘钩子为:Public Function KeyboardProc(ByVal nCode As Long, _ByVal wParam As Long, _ByVal lParam As Long) As Long如果 Code 不为 0,钩子函数必须调用 CallNextHookEx,将消息传递给下面的钩子。

wParam 和 lParam不是按键。

参数3,hmod为创建钩子函数那个实体的句柄,即你的程序本身的句柄(handle),hmod用于全局钩子,VB要实现钩子,必须设为0。

(关于句柄:每一个程序都有一个ID号称为进程,句柄则分得更细,每一个进程里的每一个控件的ID号称为句柄。

比如一个程序既有输入框又有下拉条,那么下拉条和输入框都有自己的ID号,这个称为句柄。

)参数4,dwThreadId ,为监控代码,0表示全局监控,dwThreadId用于线程钩子 VB 中可以设置为 App.ThreadID。

UnhookWindowsHookEx 函数为释放钩子,将钩子函数所占用的资源交还给系统,起一个简单的清道夫功能。

Private Declare Sub CopyMemory Lib "kernel32" _Alias "RtlMoveMemory" _(pDest As Any, _pSource As Any, _ByVal cb As Long)CopyMemory 作用是将内存里的某一块数据pSource拷贝到另一个地址pDest。

最后一个参数cb表示拷贝内容的字节大小。

Private Declare Function GetAsyncKeyState Lib "user32" _(ByVal vKey As Long) As IntegerGetAsyncKeyState作用是获得各种辅助功能键的状态(如CTRL,SHIFT什么的)。

Private Declare Function CallNextHookEx Lib "user32" _(ByVal hHook As Long, _ByVal nCode As Long, _ByVal wParam As Long, _ByVal lParam As Long) As LongCallNextHookEx 挂钩函数拦截了某条消息后,由CallNextHookEx决定是否将这些消息送还给Windows系统。

Private Type KBDLLHOOKSTRUCTvkCode As LongscanCode As Longflags As Longtime As LongdwExtraInfo As LongEnd TypeKBDLLHOOKSTRUCT为键盘钩子的结构体定义,关于该结构的成员,我没有从API函数帮助库里找到任何资源,不过大概猜也能猜出来。

成员1:vkCode为虚拟键码成员2:scanCode为扫描码成员3:flags为功能键状态成员4:扩展信息?实际上本例中我们只是需要简单的知道vkCode然后用chr函数置换成字符即可,所谓的vkCode实际上和ASCIIC码是一一对应的。

Private Const HC_ACTION = 0Private Const LLKHF_EXTENDED = &H1Private Const LLKHF_INJECTED = &H10Private Const LLKHF_ALTDOWN = &H20Private Const LLKHF_UP = &H80Private Const WH_KEYBOARD_LL = 13&Public Const VK_TAB = &H9Public Const VK_CONTROL = &H11Public Const VK_ESCAPE = &H1BPublic Const VK_DELETE = &H2E以上10个定义为常量定义,常量定义没有什么特别好说的,仅仅是帮助记忆而已。

你完全可以在使用VK_DELETE的地方使用&H2E,如果你觉得&H2E比VK_DELETE更容易理解的话...Public KeyboardHandle As Long全局变量KeyboardHandle 为键盘钩子函数句柄,这个变量在开始挂钩的时候产生,结束挂钩的时候需要用它进行清场。

好了,现在我们开始工作:首先创建一个工程,因为我们需要一个实体来运行我们的钩子函数,所以必须创建一个工程,在窗体部分填写:Private Const WH_KEYBOARD_LL = 13&Windows规定,键盘拦截的ID号为13号拦截。

Public Sub HookKeyboard()KeyboardHandle = SetWindowsHookEx( _WH_KEYBOARD_LL, AddressOf KeyboardCallback, _App.hInstance, 0&)End Sub定义一个不带参数的子程序HookKeyboard,KeyboardHandle 存储钩子函数所产生的ID号,这个在清场的时候需要用到。

SetWindowsHookEx 的4个参数,第一个,WH_KEYBOARD_LL, 正如前面定义的常量,它为13第二个,AddressOf KeyboardCallback,这是自VB5以来的一次革命,VB5之后增加了一个保留字AddressOf,它的作用是获取某一个函数的首地址指针,在VB5之前的版本是没有这个功能的,有了AddressOf,大大扩展了VB的功能,才使得VB能够调用绝大部分API函数。

因为VB本身并不存在指针。

此行的意思是获得挂接函数链的首地址。

第三个,App.hInstance,App也是VB的保留字,表示本程序本身,如App.Path表示本程序当前目录,App.hInstance表示的是本程序本身的句柄,关于句柄前面已有所描述。

第四个,0,表示全局拦截,意思就是拦截所有窗口下的键盘输入。

定义完钩子函数,下面进入函数的具体实现,这些函数属于全局函数,所以不能在窗体级定义,必须降低到“模块”级别,现创建一个模块,然后开始编写,注:在VB里调用API几乎都是模块级的,很少出现在窗体级。

实际不必深究这个问题,只要记住凡是调用API就用模块来编写就没错了。

Public Function KeyboardCallback(ByVal Code As Long, _ByVal wParam As Long, ByVal lParam As Long) As LongStatic Hookstruct As KBDLLHOOKSTRUCTIf (Code = HC_ACTION) ThenCall CopyMemory(Hookstruct, ByVal lParam, Len(Hookstruct))If (IsHooked(Hookstruct)) ThenKeyboardCallback = 1Exit FunctionEnd IfEnd IfKeyboardCallback = CallNextHookEx(KeyboardHandle, _Code, wParam, lParam)End Function函数KeyboardCallback,是整个钩子函数的核心,参数1:Code,表示拦截层次,之前我们已经说过,如果Code为0,则拦截所有窗口的键盘输入。

相关主题