| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3717 人关注过本帖
标题:我想用全局钩子拦截关机消息
只看楼主 加入收藏
weiyi75
Rank: 1
等 级:新手上路
威 望:1
帖 子:43
专家分:0
注 册:2006-9-7
收藏
 问题点数:0 回复次数:5 
我想用全局钩子拦截关机消息

子类化拦截关机消息会,但是部分程序被关闭了,看来只有全局钩子好用.

调了半天,不是崩溃了就是没拦截成功,哪个可以写个例子吗?
注意要求任何程序均不能收到关机消息退出.

搜索更多相关主题的帖子: 全局 钩子 
2006-10-09 08:21
学习VB才2天
Rank: 5Rank: 5
等 级:贵宾
威 望:16
帖 子:1653
专家分:0
注 册:2006-5-4
收藏
得分:0 

VB编程中钩子的实现及应用

前言

Windows系统中钩子具有相当强大的功能,通过这种技术可以对几乎所有的Windows 系统中的消息进行拦截、监视、处理。这种技术可以广泛应用于各种软件,尤其是需要有监控、自动记录等对系统进行监测功能的软件。本文针对这个专题进行了探讨,希望可以为读者朋友们起到抛砖引玉的作用。

一、钩子的机制及类型

Windows的应用程序都是基于消息驱动的,应用程序的操作都依赖于它所得到的消息的类型及内容。钩子与Dos中断截获处理机制有类似之处。钩子(Hook)是Windows消息处理机制的一个平台,通过安装各种钩子,应用程序可以在上面设置子程序以监视指定窗口的某种消息,并且当消息到达目标窗口之前处理它。

在Windows中,钩子有两种,一种是系统钩子(RemoteHook),它对消息的监视是整个系统范围,另一种是线程钩子(LocalHook),它的拦截范围只有进程内部的消息。对于系统钩子,其钩子函数(HookFunction)应在Windows系统的动态链接库(DLL)中实现,而对于线程钩子来说,钩子函数可以在DLL之中实现,也可以在相应的应用程序之中实现。这是因为当开发人员创建一个钩子时,Windows先在系统内存中创建一个数据结构,该数据结构包含了钩子的相关信息,然后把该结构体加到已经存在的钩子链表中去,并且新的钩子将排在老的钩子的前面。当一个事件发生时,如果安装的是一个局部钩子,当前进程中的钩子函数将被调用。如果是一个远程钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点就要求钩子函数必须在一个动态链接库中,所以如果想要使用远程钩子,就必须把该钩子函数放到动态链接库中去。对于钩子所监视的消息类型来说,Windws一共提供了如下几种类型:如表1所示:

表一、Windows消息类型

消息类型常量标识

消息类型
适用范围

WH_CALLWNDPROC
4
发给窗口的消息
线程或系统

WH_CALLWNDPROCRET
12
窗口返回的消息
线程或系统

WH_CBT
5
窗口变化、焦点设定等消息
线程或系统

WH_DEBUG
9
是否执行其它Hook的Hook
线程或系统

WH_FOREGROUNDIDLE
11
前台程序空闲
线程或系统

WH_GETMESSAGE
3
投放至消息队列中的消息
线程或系统

WH_JOURNALPLAYBACK
1
将所记载的消息进行回放
系统

WH_JOURNALRECORD
0
监视并记录输入消息
系统

WH_KEYBOARD
2
键盘消息
线程或系统

WH_MOUSE
7
鼠标消息
线程或系统

WH_MSGFILTER
-1
菜单滚动条、对话框消息
线程或系统

WH_SHELL
10
外壳程序的消息
线程或系统

WH_SYSMSGFILTER
6
所有线程的菜单滚动条、对话框消息
系统

二、VB编程中钩子的实现

(一)钩子函数(HOOK Function)的格式。Hook Function实际上是一个函数,如果是系统钩子,该函数必须放在动态链接库中。该函数有一定的参数格式,在VB中如下:

Private Function HookFunc(ByVal nCode As Long,ByVal wParam As Long,ByVal lParam As Long)As Long

其中,nCode代表是什么情况之下所产生的钩子,随钩子的不同而有不同组的可能值;参数wParam,lParam传回值包括了所监视到的消息内容,它随Hook所监视消息的种类和nCode的值不同而不同。对于用VB所设置的钩子函数,一般的框架形式如下:

Private Function HookFunc(ByVal nCode As Long,ByVal wParam As Long,ByVal lParam As Long)As Long
Select case of nCode
case ncode<0:hookfunc=callnexthookex(hHookFunc,nCode,wParam,lParam)
case值1:处理过程1:HookFunc=X1
case2:处理过程2:HookFunc=X1
……
end select
end Function

函数的传回值,如果消息要被处理,则传0,否则传1,吃掉消息。

(二)钩子的安装及执行。钩子的安装要用到几个API函数:可以使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx()函数的声明如下:

Declare function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA"(ByVal idHook As Long,ByVal lpfn As Long,ByVal hmod As Long,ByVal dwThreadId As Long)As Long

idHook值为它处理的消息类型;lpfn值为钩子子程序的地址指针。如果dwThreadId参数为0或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。除此以外,lpfn可以指向当前进程的一段钩子子程代码。hMod值为应用程序的句柄,标识包含lpfn所指的子程的DLL。如果dwThreadId标识当前进程创建的一个线程,而且子程代码位于当前进程,hMod必须为0。dwThreadId值为与安装的钩子子程相关联的线程的标识符,如果为0,钩子子程与所有的线程关联。钩子安装成功则返回钩子子程的句柄,失败返回0。

另外,一般应在钩子子程中调用CallNextHookEx()函数以执行钩子链表所指的下一个钩子子程,否则安装了别的钩子的应用程序就会收不到钩子通知,从而产生错误的结果。CallNextHookEx()函数的声明如下:

Declare Function CallNextHookEx Lib"user32" Alias "CallNextHookEx"(ByVal hHook As Long,ByVal ncode As Lonog, ByVal wParam As Long,lParam As Any)As Long

hHook值是SetWindowsHookEx()的传回值,nCode、wParam、lParam则是Hook函数中的三个参数。在程序终止之前,必须调用UnhookWindowsHookEx()函数释放与钩子关联的系统资源。UnhookWindowsEx()函数声明如下:

Declare Function Unhook WindowsHookEx Lib "user32" Alias "Unhook WindowsHookEx(ByVal hHook As Long)As Long

hHook为安装钩子时的返回值,即钩子子程的句柄。

(三)VB中钩子安装应注意的问题。lpfn参数是一个HookFunc的地址,VB规定必须将HookFunc代码放到标准的.BAS模块中,并以"Address Of HookFunc"传入,而不可以将其放到类模块中,也不能将其附加到窗体上。而对于RemoteHook来说,HookFunc应包含在动态链接库中,因此如果在VB中使用RemoteHook,则还要用到GetModuleHandle()、GetProcAddress()两个API函数,它们的声明如下:

Declare Function GetModuleHandle Lib"kernel32" Alias "GetModuleHandleA"(ByVal lpModuleName As String)As Long
Declare Function GetProcAddress Lib "kernel32" Alias "GetProcAddress"(ByVal hModule As Long,ByVal lpProcName As String)As Long

hmod值是含钩子过程的模块名柄,如果是LocalHook,该值可以是Null(VB中传0),而如果是RemoteHook,则可以使用GetModuleHandle("名称.dll")来传入。
三、实例--键盘消息的拦截

在程序开发时常用的有对输入消息进行监视的键盘钩子,对于所监视到的消息应进行处理,下面对键盘钩子参数的具体内容组成进行说明:

如果有键盘消息(WM_KEYUP或WM_KEYDOWN)将被处理时,则系统调用键盘钩子。

nCode为HC_ACTION或HC_NOREMOVE,若小于0,则要求处理函数向下传递该消息。

wParam表示按键键码常数,A键到Z键与其ASCII码的相应值''A''到''Z''是一致的,例如按C键,则wParam值为67。

lParam与WM_KEYDOWN同,占四个字节,其包括的内容较多,其二进制结构如下:

0
1
……
15
16
………
23
24
25
……
28
29
30
31

0-15位(Key repeat count),键码重复次数。16-23位(Scan code),按键的扫描码。24位(Extended_Key flag),扩展键(功能键、数字小键盘上的键)标志,为1则是扩展键,否则为0。25-28位被保留。29位(Context Code),状态描述码,ALT键被按下则为1,否则为0。30位(Previouskey_stateflag)指定先前的键状态,如果消息被发出之前键处于按下状态,则为1;键处于释放状态则为0。31位(Transiton_stateflag)状态转换标志,如果键是被按下值为1,如果键被放开值为0。

本例中的钩子用来监视并记录应用程序中的按键信息。在程序中,ALT+F4组合键被屏蔽。下面是部分代码:

Public hHook as Long
Private Sub Form_Load()′程序启动时安装钩子
hHook=SetWindowsHookEx(2,Address of MyKBHook,0,App.ThreadID)
End Sub
′具体的钩子程序,本例中该过程被包含在Module1中
Public Function MyKBHook(ByVal nCode As Long,ByVal wParam As Long,ByVal lParam As Long)As Long
If nCode>=0 then
Open "C:\Keyfile.txt" For Append As #1 ''将键盘的操作记录在Keyfile.txt文件之中
''记录所操作的键、操作时间、日期操作时的按键状态,用16进制记录
Write #1,wParam,Hex(lParam),Date,time
Close #1
MyKBHook=0 ''表示要处理这个消息
''屏蔽ALT+F4组合键
if wParam=115 And(lParam And&H20000000)<>0 Then
if(lParam And &HC000000)=0 Then ''是否进行ALT+F4操作
MyHBHook=1 ''钩子吃掉这个消息
End if
End if
End if
Call CallNextHookEx(hHook,nCode,wParam,lParam)''将消息传给下一个钩子
End Function
''程序退出时卸载钩子
Private Sub Form_Unload(Cancel As Interger)
Call Unhook WindowsHookEx(hHook)
End Sub

四、总结

钩子处理程序是Windows高级编程技术,一般程序员都使用VC++等程序设计工具实现,本文表明,对于VB来说,虽然很多人认为是非专业的设计工具,但实现钩子这样的高级技术也是非常方便的。另外在使用钩子时应注意到,钩子虽然功能比较强,但如果使用不当将会严重影响系统的效率,所以要尽量避免使用系统钩子,并且在不用钩子时,应将钩子及时卸载。


[GLOW=255,DeepPink,3]我的免费网盘[/GLOW]
2006-10-09 10:45
weiyi75
Rank: 1
等 级:新手上路
威 望:1
帖 子:43
专家分:0
注 册:2006-9-7
收藏
得分:0 
老兄,我请你写例子啊,你放资料.
2006-10-09 21:50
学习VB才2天
Rank: 5Rank: 5
等 级:贵宾
威 望:16
帖 子:1653
专家分:0
注 册:2006-5-4
收藏
得分:0 

方法(1):通过 API 函数 GetAsyncKeyState(判断函数调用时指定虚拟键的状态)、和 Timer(时间控件),来实现热键的操作
例子:用热键 F2 调用 MsgBox。(具体的例子请查看 软件报 2000 年 第38 期的文章<<利用热键操作实现一键通>>)
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Long) As Integer
Private Sub Form_Load()
Timer1.Interval = 1 注释:设置检测间隔时间
End Sub
Private Sub Timer1_Timer()
If MyHotKey(vbKeyF2) Then MsgBox "热键调用成功!!", vbOKOnly
End Sub
小结:用这个方法只需要调用一个 API 函数,比较简单,但是由于使用的是 Timer 控件来实现按键的监视,如果监视间隔时间过短,就会造成系统资源的浪费;如果间隔时间过长,按键就可能漏掉。
方法(2):通过 API 函数 RegisterHotKey(向系统注册相应的热键)、WndProc(消息处理)等等,来实现热键的操作。
例子:由于代码太多,请大家查看 软件报 2000 年 第 48 期的文章<<再谈热键编程>>
小结:这种方法需要调用调用 6 个以上的 API 函数,需要定义多个常数(操作一个热键就要定义一个常数),对于一般的编程爱好者来说,过于复杂,不好掌握。
方法(3):通过调用 VC 编写的 DLL 文件。
由于 Windows 规定用以拦截全系统键盘消息的 HookProc Callback 函数必须放在 DLL 文件里面,但 VB 无法制作 DLL 文件(注:VB 可以用来制作 ActiveX DLL 文件,但 ActiveX DLL 与单纯的 DLL 不尽相同)。
我们这里调用王国荣先生用 VC 编写的 KeybHook.dll 文件(注:现在许多的有关热键操作的软件都是调用的这个 dll 文件,大家如果需要可到 www.vbgood.com 去下载)。
例子:按键追踪程序(可以检测你按下的所有的键值)
界面如图1所示,装载 1 个 ListBox 控件,属性值为默认。
Fomr1.frm 文件代码:
Option Explicit
Private Sub Form_Load()
On Error Resume Next
注释:挂上 KeyboardHook_HookProc 函数
SetKeyboardHook Me.hWnd, WM_USER
If Err.Number <> 0 Then
MsgBox "请先将 KeybHook.dll 复制到 Windows 的所在路径!", vbCritical
End
End If
On Error GoTo 0
注释:挂上窗口程序
prevWndProc = GetWindowLong(Me.hWnd, GWL_WNDPROC)
SetWindowLong Me.hWnd, GWL_WNDPROC, AddressOf WndProc
End Sub

Private Sub Form_Unload(Cancel As Integer)
注释:卸下 KeyboardHook_HookProc 函数
ReleaseKeyboardHook
注释:卸下窗口程序
SetWindowLong Me.hWnd, GWL_WNDPROC, prevWndProc
End Sub
Module1.bas 文件代码:
Option Explicit
Public Const GWL_WNDPROC = (-4)
Public Const WM_USER = &H400
Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function SetKeyboardHook Lib "KeybHook" (ByVal hwndPost As Long, ByVal Msg As Long) As Long
Declare Function ReleaseKeyboardHook Lib "KeybHook" () As Long
Public prevWndProc As Long
Dim IsCtrlDown As Boolean

Function WndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If Msg = WM_USER Then
Form1.List1.AddItem "wParam=" & wParam & ", lParam=" & Hex(lParam)
Form1.List1.ListIndex = Form1.List1.NewIndex
If wParam = 65 And (lParam And &H80000000) <> 0 Then MsgBox "热键调用成功!!", vbOKOnly
注释:判断按键情况,对于你所按的按键的键值可通过本程序查看到
If wParam = 66 And (lParam And &H80000000) <> 0 And IsCtrlDown Then MsgBox "组合热键调用成功!!", vbOKOnly, "组合键情况"
注释:判断按键情况(组合键情况)
End If
WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam, lParam)
End Function
小结:用这种方法,就我而言感觉是最好的,可以很方便的检测按键,不需要事先用常量定义,不需要时间控件,也可方便的定义组合键。
以上程序在 VB6.0、Windows 98 编译通过,大家可根据自己的需要(自我的感觉),决定使用哪种方法,如果大家还有什么问题可到 www.d1vb.com(对编辑的话:一个 VB 论坛,我总在面)来讨论。


[GLOW=255,DeepPink,3]我的免费网盘[/GLOW]
2006-10-09 22:29
zmetero11
Rank: 1
等 级:新手上路
帖 子:123
专家分:0
注 册:2006-2-5
收藏
得分:0 
thank you!

黑色的羽翼笼罩与夜月中,守护...不变<-\^/
2006-10-09 22:45
weiyi75
Rank: 1
等 级:新手上路
威 望:1
帖 子:43
专家分:0
注 册:2006-9-7
收藏
得分:0 
2006-10-09 23:21
快速回复:我想用全局钩子拦截关机消息
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018473 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved