IV. Implémentation▲
IV-A. Introduction▲
Nous allons présenter ici, les différentes API mises en oeuvre dans le cadre de la création d'un Hook.
IV-B. SetWindowsHookEx▲
IV-B-1. Présentation▲
Cette API permet la mise en place d'une procédure de traitement au sein d'une des chaînes de traitement de messages :
<
DllImport
(
"user32"
)>
_
Public
Shared
Function
SetWindowsHookEx
(
_
ByVal
idHook As
Integer
, _
ByVal
lpfn As
HookProc, _
ByVal
hMod As
IntPtr, _
ByVal
dwThreadId As
Integer
) As
Integer
End
Function
La procédure de traitement installée par le Hook l'est toujours en début de chaîne.
(Cela signifie que c'est toujours la procédure de traitement installée
par le dernier Hook qui est déclenchée la première).
IV-B-2. Paramètres▲
IV-B-2-a. idHook▲
Il s'agit du type de Hook à mettre en place :
- Hook sur les messages clavier (WH_KEYBOARD_LL et WH_KEYBOARD),
- Hook sur les messages souris (WH_MOUSE_LL et WH_MOUSE),
- Hook sur les messages de création/destruction de fenêtres de premier plan (WH_SHELL),
- etc.
IV-B-2-b. lpfn▲
C'est un pointeur sur la procédure de traitement à inscrire dans la chaîne de traitement.
Comme pour toute utilisation d'un délégué passé à du code non managé,
l'application devra s'assurer du maintien du pointeur sur ce délégué
afin d'éviter sa collecte inopinée par le Garbage Collector.
Le passage d'un "AddressOf NomDeLaProcédure" est donc à proscrire, et il
convient d'utiliser une fonction déléguée déclarée explicitement.
La signature de la procédure de traitement ne dépendant pas du type de Hook, on pourra utiliser le délégué suivant :
Public
Delegate
Function
HookProc _
(
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
Les valeurs des paramètres passés à la fonction seront par contre dépendantes du type de Hook.
Cette procédure pourra retourner une valeur différente de 0 pour indiquer au système que le message a été traité (celui-ci ne communiquera alors pas ce message à la fenêtre cible).
Toutefois, elle ne devra traiter le message que si le paramétre ncode est supérieur à 0. Dans le cas contraire, elle devra retourner le résultat de l'appel à l'API CallNextHookEx (décrite plus loin).
IV-B-2-c. hMod▲
C'est le Handle de la DLL contenant la procédure pointée par lpfn.
Ce paramètre peut être omis (par cela entendez, valorisé à IntPtr.Zero)
dans le cas où cette procédure de traitement se trouve
dans le même module que l'installation via SetWindowsHookEx.
Ceci est vrai qu'il s'agisse d'un Hook Global ou d'un Hook Local.
A titre personnel, je n'utilise donc pas ce paramètre car il me semble opportun de gérer la mise en place et la suppression du Hook au travers d'un objet levant au besoin un événement. L'ensemble du code de Hooking s'en trouve donc intégralement dans le même module.
IV-B-2-d. dwthreadId▲
C'est l'identifiant du thread pour lequel le Hook est positionné.
Ce paramètre valorisé à 0 équivaut à positionner un Hook pour l'ensemble des
threads.
Donc :
- Si dwthreadId est alimenté avec l'identifiant d'un thread, il s'agira d'un Hook Local, i.e. localisé sur ce thread,
- Si dwthreadId n'est pas défini (0), il s'agira d'un Hook Global, i.e. pour l'ensemble des threads.
IV-B-3. Valeur retournée▲
Si le positionnement de la procédure de traitement au sein de la chaîne a
réussi, SetWindowsHookEx retourne le Handle de la procédure de Hook.
En cas d'échec, la valeur retournée est 0 et le code erreur peut être obtenu
via l'API GetLastError.
<
DllImport
(
"kernel32"
)>
_
Public
Shared
Function
GetLastError
(
) As
Integer
End
Function
IV-B-4. Utilisation▲
Dans le cadre d'un Hook souris global, l'utilisation de SetWindowsHookEx peut se présenter ainsi :
Public
Delegate
Function
HookProc
(
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
Private
Shared
hMouseHook As
Integer
Private
Shared
MouseHookProcedure As
HookProc
<
DllImport
(
"user32"
)>
_
Public
Shared
Function
SetWindowsHookEx
(
_
ByVal
idHook As
Integer
, _
ByVal
lpfn As
HookProc, _
ByVal
hMod As
IntPtr, _
ByVal
dwThreadId As
Integer
) As
Integer
End
Function
<
DllImport
(
"kernel32"
)>
_
Public
Shared
Function
GetLastError
(
) As
Integer
End
Function
#Region
"Declaration constantes"
Protected
Const
WH_MOUSE_LL As
Integer
=
14
#End
Region
Protected
Sub
StartHookingInternal
(
)
' Evite le CallbackOnCollectedDelegate
MouseHookProcedure =
New
HookProc
(
AddressOf
MouseHookProc)
hMouseHook =
SetWindowsHookEx
(
WH_MOUSE_LL, MouseHookProcedure, _
IntPtr.Zero
, 0
)
If
hMouseHook =
0
Then
MessageBox.Show
(
"Echec de l'installation du hook : "
&
GetLastError
(
))
Else
MessageBox.Show
(
"Start effectué"
)
End
If
End
Sub
Private
Function
MouseHookProc
(
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
' Traitement
' --> ici
End
Function
IV-C. CallNextHookEx▲
IV-C-1. Présentation▲
Cette API permet de passer les informations du message en cours de traitement à la procédure suivante dans la chaîne de traitement.
<
DllImport
(
"user32"
)>
_
Public
Shared
Function
CallNextHookEx
(
_
ByVal
hhk As
Integer
, _
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
End
Function
L'appel à cette fonction doit être réalisé dans la procédure de
traitement positionnée par SetWindowsHookEx (c'est à dire dans la
procédure déléguée).
Sa valeur de retour correspond à la réponse fournie par la
procédure de traitement du Hook suivant de la chaîne.
Une procédure de traitement retournera une valeur différente de 0 pour indiquer au système que le message a été traité et qu'il n'est pas nécessaire de le communiquer à la fenêtre cible.
En l'absence d'un blocage nécessaire du message dans la procédure de
traitement de notre Hook, celle-ci doit rendre le résultat fourni par la
procédure de traitement du Hook suivant de la chaîne.
Une chaîne de traitement non bloquante pour le message peut donc se
représenter ainsi :
Ainsi l'utilisation de CallNextHookEx peut de manière générale se décliner à partir de :
Public
Delegate
Function
HookProc _
(
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
Private
InternalHookProcedure As
HookProc
Private
Shared
hHook As
Integer
Protected
Sub
StartHookingInternal
(
)
InternalHookProcedure =
New
HookProc
(
AddressOf
InternalHookProc)
hHook =
SetWindowsHookEx
(
xxxxxx , MouseHookProcedure, _
yyyy , zzzzz )
End
Sub
Private
Function
InternalHookProc
(
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
' Traitement du message ici
Return
CallNextHookEx
(
hHook, nCode, wParam, lParam)
End
Function
Se passer de l'appel à CallNextHookEx est fortement déconseillé.
En effet, on ne sait pas dans quelle position de la chaîne se trouve notre Hook
et il est donc impossible de déterminer à priori l'effet du non déclenchement
des procédures de traitement positionnées en aval de celui-ci par les autres
applications.
Ceci sera illustré dans la suite de l'article.
IV-C-2. Paramètres▲
Les paramètres utilisés sont ceux obtenus par la procédure de traitement auxquels s'ajoute le paramètre hhk qui doit être alimenté avec le Handle de le procédure de Hook courant (retourné par SetWindowsHookEx)
IV-C-3. Valeur retournée▲
La valeur de retour correspond à la réponse fournie par la procédure de traitement du Hook suivant de la chaîne.
IV-C-4. Utilisation▲
Dans le cadre de notre exemple pour un Hook souris global, l'utilisation de CallNextHookEx peut se présenter ainsi :
<
DllImport
(
"user32"
)>
_
Public
Shared
Function
CallNextHookEx
(
_
ByVal
hhk As
Integer
, _
ByVal
nCode As
Integer
, _
ByVal
wParam As
Integer
, _
ByVal
lParam As
IntPtr) As
Integer
End
Function
Private
Function
MouseHookProc
(
ByVal
nCode As
Integer
,
ByVal
wParam As
Integer
,
ByVal
lParam As
IntPtr) As
Integer
' Traitement
' --> ici
Return
CallNextHookEx
(
hMouseHook, nCode, wParam, lParam)
End
Function
IV-D. UnhookWindowsHookEx▲
IV-D-1. Présentation▲
Cette API permet de retirer la procédure de traitement de la chaîne de procédures de traitement .
Toute installation d'un Hook via SetWindowsHookEx doit être
suivie d'une désinstallation via UnhookWindowsHookEx, car
le Hook génère un ralentissement du traitement des messages systèmes.
Les règles de "bonne conduite" à retenir sont donc :
- Installer le Hook le plus TARD possible via SetWindowsHookEx,
- Désinstaller le Hook le plus TOT possible via UnhookWindowsHookEx.
<
DllImport
(
"user32"
)>
_
Public
Shared
Function
UnhookWindowsHookEx
(
ByVal
idHook As
Integer
) As
Boolean
End
Function
IV-D-2. Paramètres▲
UnhookWindowsHookEx accepte un seul paramètre hhk définissant le Handle de le procédure de Hook à désinstaller (retourné par SetWindowsHookEx)
IV-D-3. Valeur retournée▲
La fonction retourne True si la désinstallation a réussi, False sinon.
IV-C-4. Utilisation▲
Dans le cadre de notre exemple pour un Hook souris global, l'utilisation de UnhookWindowsHookEx peut se présenter ainsi :
<
DllImport
(
"user32"
)>
_
Public
Shared
Function
UnhookWindowsHookEx
(
ByVal
idHook As
Integer
) As
Boolean
End
Function
Protected
Overrides
Sub
StopHookingInternal
(
)
Dim
blnUH As
Boolean
=
True
blnUH =
UnhookWindowsHookEx
(
hMouseHook)
MyBase
.OnMouseLog
(
"Stop effectué"
)
End
Sub