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 IntegerLes 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 FunctionIV-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 FunctionIV-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 FunctionIV-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 FunctionIV-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

