把鼠标关标滑过一个窗口时,该窗口的有关消息将显示在主窗口中。当您按下“Unhook”时,应用程序将卸载钩子。主窗口使用一个对话框来作为它的主窗口。它自定义了一个消息WM_MOUSEHOOK,用来在主窗口和DLL之间传递消息。当主窗口接收到该消息时,wParam中包含了光标所在位置的窗口的句柄。当然这是我们做的安排。我这么做只是为了方便。您可以使用您自己的方法在主应用程序和DLL之间进行通讯。 CW&.NT
&;&ho+qD
.if HookFlag==FALSE $za8"T*I
l{j~Q^U})
invoke InstallHook,hDlg E>|: D
iBTYY{-wF
.if eax!=NULL sEJC-$
*)M49a*UD
mov HookFlag,TRUE q
11IkDa
/U&Opo
{aO
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText {X(:jAy
G` XC
.endif %a+mk
E
.mLK`c6
G s+3e8
?W^c4NtP
该应用程序有一个全局变量,HookFlag,它用来监视钩子的状态。如果安装来钩子它就是TRUE,否则是FALSE。当用户按下Hook按钮时,应用程序检查钩子是否已经安装。如果还没有的话,它将调用DLL中引出的函数InstallHook来安装它。注意我们把主对话框的句柄传递给了DLL,这样这个钩子DLL就可以把WM_MOUSEHOOK消息传递给正确的窗口了。当应用程序加载时,钩子DLL也同时加载。时机上当主程序一旦加载到内存中后,DLL就立即加载。DLL的入口点函数载主程序的第一条语句执行前就前执行了。所以当主程序执行时,DLL已经初始化好了。我们载入口点处放入如下代码: C?Bl{4-P}*
aE;le{|!({
i0$Bx>
}XO K,Hw
.if reason==DLL_PROCESS_ATTACH #S&Tkip]"W
.sC?7O=
push hInst =U%Rvm
?=m?jNa;nC
pop hInstance 26&'X+n&
S"wR%\NIp
.endif ?qCK7$j
ho##Z*O
/x_o!<M
&e6CJ
该段代码把DLL自己的实例句柄放到一个全局变量中保存。由于入口点函数是在所有函数调用前被执行的,所以hInstance总是有效的。我们把该变量放到.data中,使得每一个进程都有自己一个该变量的值。因为当鼠标光标停在一个窗口上时,钩子DLL被映射进进程的地址空间。加入在DLL缺省加载的地址处已经加载其它的DLL,那钩子DLL将要被映射到其他的地址。hInstance将被更新成其它的值。当用户按下Unhook再按下Hook时,SetWindowsHookEx将被再次调用。这一次,它将把新的地址作为实例句柄。而在例子中这是错误的,DLL装载的地址并没有变。这个钩子将变成一个局部的,您只能钩挂发生在您窗口中的鼠标事件,这是很难让人满意的。 ])xx<5Jt4
h 3]wL.V
3)\8%Ox
]E\n9X-{
InstallHook proc hwnd:DWORD UfN&v >8f
Og,$ sH}`
push hwnd BZzrRC
Tyvtmx M
pop hWnd r E}%KsZ
_%5Ro6
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL "@??Fw!
)+:EJH~
mov hHook,eax M$Zcn# A
0TQ$C-%
ret O3I8k\`
h }B%
/U
InstallHook endp :xtXQza"-
0NS<?p~_S
;W>k@L
a)wJT`xu
InstallHook 函数非常简单。它把传递过来的窗口句柄保存在hWnd中以备后用。接着调用SetWindowsHookEx函数来安装一个鼠标钩子。该函数的返回值放在全局变量hHook中,将来在UnhookWindowsHookEx中还要使用。在调用SetWindowsHookEx后,鼠标钩子就开始工作了。无论什么时候发生了鼠标事件,MouseProc函数都将被调用: 6
J{k(H$3
!o:f$6EA~C
;kY(<{ 2
-i0~]*
MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD O^oWG&Y;v
7=;R& mqC
invoke CallNextHookEx,hHook,nCode,wParam,lParam xai*CY@cQ
9I&xfvD,
mov edx,lParam "wNJ
rJGf.qJJ
assume edx:PTR MOUSEHOOKSTRUCT gIjh:_ Pz
,yiX# ;j
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y DGS $Ukz&T
vN`klDJgW[
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 &J+CSv,39
<
jJ
assume edx:nothing gt@m?w(
MF5[lK9e
xor eax,eax ML|FQ
%J+E/
ret <g"{Wv: h
SLa>7`<Q
MouseProc endp U~:-roQ(\
YNQY4\(
U/!TKic+
E|iQc8gr&
钩子函数首先调用CallNextHookEx函数让其它的钩子处理该鼠标事件。然后,调用WindowFromPoint函数来得到给定屏幕坐标位置处的窗口句柄。注意:我们用lParam指向的MOUSEHOOKSTRUCT型结构体变量中的POINT成员变量作为当前的鼠标位置。在我们调用PostMessage函数把WM_MOUSEHOOK消息发送到主程序。您必须记住的一件事是:在钩子函数中不要使用SendMessage函数,它会引起死锁。MOUSEHOOKSTRUCT的定义如下: f4fvrL
LY%WD%pL
YtmrRDQs
=l+yA>t|
MOUSEHOOKSTRUCT STRUCT DWORD 6 (]Dh;gC
fdFo# P
pt POINT <> hgPa6Kd
k>;`FFQU>
hwnd DWORD ? ZrpU <
*\
R ]NV
wHitTestCode DWORD ? EJMM9(DQ7
F3On?x)
dwExtraInfo DWORD ? MTuV^0%jD
~%<X0s|
MOUSEHOOKSTRUCT ENDS ,E S0NA
>*35C`^
wW>A_{Y
+^60T$
LSL/ZvSP
)_YX DU
pt 是当前鼠标所在的屏幕位置。 f6hnTbJ
h4fJvOk|!
hwnd 是将接收鼠标消息的窗口的句柄。通常它是鼠标所在处的窗口,但是如果窗口调用了SetCapture,鼠标的输入将到向到这个窗口。因我们不用该成员变量而是用WindowFromPoint函数。 oB(?_No7
(7wc *#}
wHitTestCode 指定hit-test值,该值给出了更多的鼠标位置值。它指定了鼠标在窗口的那个部位。该值的完全列表,请参考WIN32 API 指南中的WM_NCHITTEST消息。 oH97=>
6]K_m(F
dwExtraInfo 该值包含了相关的信息。一般该值由mouse_event函数设定,可以调用GetMessageExtraInfo来获得。 ;`0%t$@-
em%4Ap
fK>L!=Q
5-:?&|JK;
-_=nDH
f,U.7E
当主窗口接收到WM_MOUSEHOOK 消息时,它用wParam参数中的窗口句柄来查询窗口的消息。 \V;F/Zy(
ZgTW.<.%2
` Fa~
$*^7iT4q_t
.elseif uMsg==WM_MOUSEHOOK ]E5o1eeg
)}Hpi<5N
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 D+rxT:
d
)1?y 8_B
invoke wsprintf,addr buffer,addr template,wParam YL!P0o13r
(nQ^
invoke lstrcmpi,addr buffer,addr buffer1 ;AG8C#_
~[t[y~Hup
.if eax!=0 h79}qU
/CrSu
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer lU8l}Ndz"
*3+4[WT0]a
.endif R$R *'l
j\eI0b @*
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 ^um<bWNc
sA+ }TNhq
invoke GetClassName,wParam,addr buffer,128 g@d*\ P)
wW Lj?;bx
invoke lstrcmpi,addr buffer,addr buffer1 0_jf/an,%
[:7'?$
.if eax!=0 M[NV)q/)
nGC/R&
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer {a =#B)6
ld[I}88$
.endif [*Z;\5&P
sU=H&D99
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 &sl0W-;0
bTI|F]^!
invoke GetClassLong,wParam,GCL_WNDPROC esJ~;~[@(r
p'Y^X
invoke wsprintf,addr buffer,addr template,eax (^ JI%>
M`0V~P`^
invoke lstrcmpi,addr buffer,addr buffer1 atj(eg
T6'^EZZY
.if eax!=0 ><4<yj1
EfqX
y>W
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer ,<.V7(|t)
&j;wCvE4+
.endif M$wC=b
Jx:Y-$
kBS9tKBWg
&w\{TZ{
为了避免重绘文本时的抖动,我们把已经在编辑空间中线时的文本和我们将要显示的对比。如果相同,就可以忽略掉。得到类名调用GetClassName,得到窗口过程调用GetClassLong并传入GCL_WNDPROC标志,然后把它们格式化成文本串并放到相关的编辑空间中去。 Lw1Yvtn
<3nMx^
Usvl}{L[
P1!qbFDv8
invoke UninstallHook {$oj.V 4
VG5i{1
0
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText #`^}PuQ
H8=N@l
mov HookFlag,FALSE GC-5X`Sq
`>o{P/HN
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL KR}?H#%
fuW\bo3
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL !t"4!3
Dm981t>wL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL XPc^Tq
i$Ul(?
.xCZ1|+gG
9X6h
当用户按下Unhook后,主程序调用DLL中的UninstallHook函数。该函数调用UnhookWindowsHookEx函数。然后,它把按钮的文本换回“Hook”,HookFlag的值设成FALSE再清除掉编辑控件中的文本。 1C+13LE$U
p
T?}Kc
链接器的开关选项如下: RHW]Z
Pr<
}RF(CwZr(
)$2QZ
qX
f_OQ./`
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS 8S
TvCH"Z_
#\{l"-
H*n-_{h"t
C[cbbp
它指定.bss段作为一个共享段以便所有映射该DLL的进程共享未初始化的数据段。如果不用该开关,您DLL中的钩子就不能正常工作了。