Tutorial 24: Windows Hooks
(Hook=Crochet)

Nous allons voir ce que sont les Windows Hooks dans ce Tutorial. Les Windows Hooks sont très puissants. Grâce à eux, vous pouvez fouiner à l'intérieur d'autres processus (programmes) et quelques fois changer leurs comportements.
Downloadez l'exemple ici.

Théorie:

Les Windows Hooks peuvent être considérés comme une des particularités les plus puissantes de Windows. Avec elles, vous pourrez prendre au piège les événements qui se produisent dans votre propre process ou bien dans d'autres processus. (exemple vous verrez apparaître dans votre fenêtre le n° qui sert d'handle à un bouton ou à une boîte se saisie…ça sert à ça un Hook). En "accrochant" (en Hookant), vous informez Windows sur les caractéristiques de la fonction Hookée. Notre fonction de filtre est aussi appelée la procédure d'hook, laquelle sera appelée à chaque fois qu'un événement intéressant se produit. Il y en a deux types en fait : les Hooks locaux et éloignés. Quand vous mettez en place des Hooks, rappelez-vous qu'ils ont une influence sur les performances du système. Les 'System-Wide Hooks' sont tristement célèbres. Puisque TOUS les événements de liens seront mis en déroute par votre fonction de filtre (votre procédure d'Hooks), votre système peut être relativement ralentir. Ainsi si vous employez un 'System-Wide Hook', vous devez l'utiliser judicieusement et non pas l'accrocher aussitôt même si vous n'en avez pas encore besoin. Donc, vous avez de grandes chances de faire planter les autres processus puisque vous pouvez toucher au fonctionnement d'autres processus et si il y a quelque chose d'incorrecte dans votre fonction de filtre, il se peut que les autres programmes restent en attente. Souvenez-vous en : 'le pouvoir vient de paire avec la responsabilité'.
Vous devez vraiment comprendre le fonctionnement d'un hook avant que vous ne puissiez l'employer efficacement. Quand vous créez un hook, Windows crée une structure de données dans la mémoire, contenant l'information sur le hook et l'ajoute à une liste liée de Hooks existant. Le nouvel hook est ajouté devant les vieux Hooks. Lorsqu'un événement arrive, si vous faites appel à un hook local, la fonction de filtre de votre programme sera appelée, donc c'est plus direct. Mais si c'est un hook éloigné, le système doit injecter le code pour la procédure d'hook dans l'espace d'adresse d'un autre process. Et le système peut faire ça uniquement si la fonction se trouve dans un DLL. Ainsi, si vous voulez employer un hook éloigné, votre procédure d'hook doit résider dans un DLL. Il y a deux exceptions à cette règle: l'écriture d'un journal et la lecture d'un journal d'Hooks. Les procédures d'hook pour ce deux Hooks doivent résider dans le lien qui mettent en place les Hooks. La raison pourquoi çà doit être ainsi c'est que : les deux Hooks fonctionnent avec des interceptions de bas niveau d'événements d'entrée matériel. Les événements d'entrée doivent être (recorded/playbacked) enregistrés/lus dans l'ordre où ils sont apparus. Si le code de ces deux Hooks sont dans un DLL, les événements d'entrée peuvent se disperser parmi plusieurs liens et il est impossible de connaître leur ordre les uns par rapport aux autres. Donc la solution c'est que : la procédure d'hook de ce deux Hooks doit être dans un lien simple seulement c'est-à-dire le lien qui installe les Hooks.
Il y a 14 types d'Hooks :
  • WH_CALLWNDPROC est appelé quand SendMessage est appelé
  • WH_CALLWNDPROCRET est appelé lors du retours de SendMessage.
  • WH_GETMESSAGE est appelé quand GetMessage ou PeekMessage sont appelés
  • WH_KEYBOARD est appelé quand GetMessage ou PeekMessage récupèrent le message WM_KEYUP ou WM_KEYDOWN dans la file d'attente (des messages).
  • WH_MOUSE est appelé quand GetMessage ou PeekMessage récupère un message provenant de la souris dans la file d'attente des messages.
  • WH_HARDWARE est appelé quand GetMessage ou PeekMessage récupère certains messages en provenance de votre matériel, mais qui n'ont pas de rapport avec le clavier ou la souris.
  • WH_MSGFILTER est appelé pour une DialogBox, quand son menu ou un scrollbar est sur le point de traiter un message. Cet hook est local. Il a été spécifiquement conçu pour des objets qui ont leurs propres boucles de message internes.
  • WH_SYSMSGFILTER c'est la même chose que pour WH_MSGFILTER mais pour un système-large (system-wide).
  • WH_JOURNALRECORD est appelé quand Windows récupère le message dans la file d'attente d'une entrée matériel.
  • WH_JOURNALPLAYBACK est appelé quand un événement est requit dans la file d'attente d'entrée matériel du système.
  • WH_SHELL est appelé quand quelque chose d'intéressant provenant du Sell arrive, comme quand la barre de tâche a besoin de rafraîchir (redessiner) ses boutons.
  • WH_CBT est spécialement employé pour la formation assistée par ordinateur. (CBT)
  • WH_FOREGROUNDIDLE est employé intérieurement par Windows. C'est vraiment très peu utilisé pour applications générales.
  • WH_DEBUG est employé pour 'debugger' la procédure se faisant Hooker.
Maintenant que nous avons pris connaissance de la théorie, nous pouvons nous concentrer sur le fait de savoir comment installer/désinstaller des Hooks.
Pour installer un hook, on appelle la fonction SetWindowsHookEx qui a la syntaxe suivante :
SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORD
  • HookType est une des valeurs inscrites ci-dessus, par exemple,WH_MOUSE, WH_KEYBOARD
  • pHookProc est l'adresse de la procédure d'hook qui sera appelée pour traiter les messages du hook spécifié. Si c'est un hook éloigné, il doit résider dans un DLL. Si ce n'est pas le cas, il doit appartenir à votre prog.
  • hInstance est l'instance handle du DLL dans lequel la procédure d'hook réside. Si le hook est local, cette valeur doit être NULL.
  • ThreadID est l'ID du lien sur lequel vous aimeriez installer votre hook pour l'espionner. C'est ce paramètre qui détermine si le hook est local ou éloigné. Si ce paramètre est NULL, Windows interprétera votre hook comme un hook système-large éloigné qui s'attaque à tous les liens, dans le système. Si vous spécifiez l'ID d'un lien de votre propre processus (programme), cet hook est local. Si vous spécifiez l'ID d'un autre processus, votre hook est un lien-spécifique éloigné. Il y a deux exceptions à cette règle :WH_JOURNALRECORD et WH_JOURNALPLAYBACK sont toujours des 'system-wide hooks' locaux (système-large), lequels n'ont pas besoin d'être dans un DLL. Et WH_SYSMSGFILTER est toujours un 'system-wide hook' éloigné. En réalité il est identique au hookWH_MSGFILTER avec ThreadID=0 (avec pour ID du lien une valeur NULL).
si l'appel est couronné de succès, il renvoie l'handle du hook dans eax. Si c'est pas le cas, un NULL est renvoyé dans eax. Vous devez sauvegarder l'handle du hook pouvoir 'déhooker' plus tard.
Vous pouvez désinstaller votre hook en appelant UnhookWindowsHookEx lequel accepte qu'un seul paramètre, l'handle du hook que vous voulez désinstaller. Si l'appel réussit, il renvoie une valeur non-nulle dans eax. Sinon, il renvoie le NULL (forcément).
Maintenant que vous savez comment installer/désinstaller des Hooks, nous pouvons examiner la procédure d'hook en elle-même.
La procédure d'hook sera appelée à chaque fois qu'un événement étant associé au type de hook que vous a installé, arrive. Par exemple, si vous installez le hook WH_MOUSE quand un événement de souris arrive, votre procédure d'hook sera appelée. Indépendamment du type de hook que vous avez installé, la procédure d'hook a toujours le prototype suivant :
    HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD
     
    • nCode indique le code du hook.
    • wParam et lParam contiennent une information complémentaire à propos de l'événement.
HookProc est en réalité le détenteur du nom de la fonction. Vous pouvez le nommer comme ça vous chante tant qu'il a le prototype vu ci-dessus. L'interprétation de nCode, wParam et lParam dépend du type de hook que vous avez installé. Donc de la valeur de retour de la procédure d'hook. Par exemple :
WH_CALLWNDPROC
  • nCode peut seulement être HC_ACTION ce qui signifie qu'il y a un message envoyé à une fenêtre.
  • wParam contient le message qui vient d'être envoyé, si ce n'est pas le cas il prendra la valeur zéro.
  • lParam pointe sur la structure CWPSTRUCT.
  • La valeur de retour: si on ne l'emploie pas , on gardera un retour NULL.
WH_MOUSE
  • nCode peut être soit HC_ACTION soit HC_NOREMOVE.
  • wParam contient le message de la souris.
  • lParam pointe sur la structure MOUSEHOOKSTRUCT.
  • La valeur de retour: est '0' si le message doit être traité. Et '1' si le message doit être abandonné.
Vous devez consulter votre référence win32 api pour de plus amples détails à propos de la signification des paramètres et de la valeur de retour des hooks que vous souhaitez installer.
Maintenant il reste un petit truc à comprendre à propos de la procédure d'hook. Rappelez-vous que votre Hook est mis à la suite et en tête de la liste des hooks installés les plus récemment (dans une liste de liée à votre prog). Quand un événement arrive, Windows appellera uniquement le premier hook de la liste. C'est le devoir de votre procédure d'hook d'appeler le hook suivant dans la liste. Vous pouvez choisir de ne pas appeler le hook suivant mais vous, mieux mieux que quiconque, savez ce que vous faites. La plupart du temps, c'est une bonne chose d'appeler la procédure suivante, comme ça les autres Hooks sont prêts. Vous pouvez appeler le hook suivant en appelant CallNextHookEx lequel possède le prototype suivant :
CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD
  • hHook est l'handle de votre propre hook. La fonction emploie cet handle pour fouiller la liste et chercher la procédure d'hook qu'elle doit appeler ensuite.
  • nCode, wParam et lParam sont seulement trois valeurs que vous avez la possibilité de passer à d'autres fonctions. On les reçoit après l'appel: CallNextHookEx.
Un point important sur les Hooks éloignés : la procédure d'hook doit résider dans un DLL lequel sera 'mappé' (configuré) dans d'autres processus. (Voir le tutorial 13). Lorsque Windows 'mappe' le DLL dans d'autres processus, il ne 'mappera' pas la section des données à l'intérieur de ces autres processus. Bref, tous les processus (les autres programmes) partagent une unique copie du code mais ils auront leur propre copie privée de la section des données du DLL! Ça peut être une surprise de taille pour les imprudents. Vous pouvez penser que lorsque vous stockez une valeur dans une variable appartenant à la section des données du DLL, que cette valeur sera partagée entres chacun des processus qui font appel à ce DLL., mais ce n'est pas tout à fait vrai. Dans une situation normale, ça se passe bien comme ça puisque on a l'illusion que chaque processus a sa propre copie du DLL. Mais ce n'est pas le cas quand hook de Windows est concerné. Nous souhaitons que le DLL soit identiques pour tous les processus, incluant les données. La solution : on doit marquer la section des données en tant que section partagée. On peut faire ça en spécifiant l'attribut de section dans le commutateur de linker. Pour MASM, vous avez besoin d'employer ce commutateur :
/SECTION:<section name>, S
Le nom de la section des données initialisées est .data et les données non initialisées sont .bss. Par exemple si vous voulez assembler un DLL qui contient une procédure d'hook et que vous voulez que la section des données non initialisée soit partagée entre plusieurs processus, vous devez employer la ligne suivante :
link /section:.bss,S  /DLL  /SUBSYSTEM:WINDOWS ..........
L'attribut 'S' marque la section en tant que section partagée.

Exemple:

En fait il faut deux modules : Il y a le programme principal qui affichera les éléments graphiques du GUI (comme d'habitude, ex : les boutons la zone de saisie…) et l'autre partie c'est le DLL qui installera/désinstallera le hook.

;--------------------------------------------- Voici le code source du programme principal --------------------------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include mousehook.inc
includelib mousehook.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf TEXTEQU <wsprintfA>

.const
IDD_MAINDLG                   equ 101
IDC_CLASSNAME              equ 1000
IDC_HANDLE                     equ 1001
IDC_WNDPROC                 equ 1002
IDC_HOOK                         equ 1004
IDC_EXIT                           equ 1005
WM_MOUSEHOOK             equ WM_USER+6

DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
HookFlag dd FALSE
HookText db "&Hook",0
UnhookText db "&Unhook",0
template db "%lx",0

.data?
hInstance dd ?
hHook dd ?
.code
start:
    invoke GetModuleHandle,NULL
    mov hInstance,eax
    invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL
    invoke ExitProcess,NULL

DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    LOCAL hLib:DWORD
    LOCAL buffer[128]:byte
    LOCAL buffer1[128]:byte
    LOCAL rect:RECT
    .if uMsg==WM_CLOSE
        .if HookFlag==TRUE
            invoke UninstallHook
        .endif
        invoke EndDialog,hDlg,NULL
    .elseif uMsg==WM_INITDIALOG
        invoke GetWindowRect,hDlg,addr rect
        invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW
    .elseif uMsg==WM_MOUSEHOOK
        invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
        invoke wsprintf,addr buffer,addr template,wParam
        invoke lstrcmpi,addr buffer,addr buffer1
        .if eax!=0
            invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
        .endif
        invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
        invoke GetClassName,wParam,addr buffer,128
        invoke lstrcmpi,addr buffer,addr buffer1
        .if eax!=0
            invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
        .endif
        invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
        invoke GetClassLong,wParam,GCL_WNDPROC
        invoke wsprintf,addr buffer,addr template,eax
        invoke lstrcmpi,addr buffer,addr buffer1
        .if eax!=0
            invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
        .endif
    .elseif uMsg==WM_COMMAND
        .if lParam!=0
            mov eax,wParam
            mov edx,eax
            shr edx,16
            .if dx==BN_CLICKED
                .if ax==IDC_EXIT
                    invoke SendMessage,hDlg,WM_CLOSE,0,0
                .else
                    .if HookFlag==FALSE
                        invoke InstallHook,hDlg
                        .if eax!=NULL
                            mov HookFlag,TRUE
                            invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
                        .endif
                    .else
                        invoke UninstallHook
                        invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
                        mov HookFlag,FALSE
                        invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
                        invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
                        invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL
                    .endif
                .endif
            .endif
        .endif
    .else
        mov eax,FALSE
        ret
    .endif
    mov eax,TRUE
    ret
DlgFunc endp

end start

;--------------------------------------------------- Et voici enfin le code source du DLL ------------------------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.const
WM_MOUSEHOOK equ WM_USER+6

.data
hInstance dd 0

.data?
hHook dd ?
hWnd dd ?

.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
    .if reason==DLL_PROCESS_ATTACH
        push hInst
        pop hInstance
    .endif
    mov  eax,TRUE
    ret
DllEntry Endp

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
    invoke CallNextHookEx,hHook,nCode,wParam,lParam
    mov edx,lParam
    assume edx:PTR MOUSEHOOKSTRUCT
    invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
    invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
    assume edx:nothing
    xor eax,eax
    ret
MouseProc endp

InstallHook proc hwnd:DWORD
    push hwnd
    pop hWnd
    invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
    mov hHook,eax
    ret
InstallHook endp

UninstallHook proc
    invoke UnhookWindowsHookEx,hHook
    ret
UninstallHook endp

End DllEntry

;--------------------------------C'est le makefile du DLL (le moyen de l'assembler)------------------------------

NAME=mousehook
$(NAME).dll: $(NAME).obj
        Link /SECTION:.bss,S  /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib $(NAME).obj
$(NAME).obj: $(NAME).asm
        ml /c /coff /Cp $(NAME).asm
 

Analyse:

Cet exemple ouvrira une boîte de dialogue avec trois commandes d'éditions dans lesquels seront affichés : le nom de classe (Class Name), l'handle de la fenêtre (ou de l'objet) et l'adresse de la procédure de fenêtre (Window Proc). Ces 3 paramètres n'appartiendront pas forcément à votre propre programme, mais plus généralement à n'importe quel objet de n'importe quelle fenêtre sur lequel passe votre curseur de souris. Il y a deux boutons, 'hook' et 'Exit'. Quand vous appuyez sur le bouton 'hook', le programme Hooke l'entrée souris, et le texte du bouton se modifie en 'Unhook'. Quand vous déplacez le curseur de la souris vers une autre fenêtre ou sur un objet quelconque, les renseignements de cette fenêtre seront affichés dans votre fenêtre principale (votre DialogBox). Lorsque vous appuyez sur le bouton 'Unhook', le programme arrête de hooker le objets qui se trouvent sous la souris.
Ce programme utilise une boîte de dialogue en tant que fenêtre principale. On définit un message construit sur mesure (Custum) WM_MOUSEHOOK, lequel sera employé entre le programme principal et son DLL qui contient la fonction d'hook. Lorsque le programme principal reçoit ce message, wParam contient l'handle de la fenêtre (ou de l'objet) sur lequel votre curseur de souris est placé. Bien sûr, c'est un choix arbitraire. J'ai décidé d'envoyer la valeur de l'handle dans wParam pour plus de simplicité. Mais vous pouvez très bien choisir votre propre méthode de communication entre le programme principal et le DLL (contenant le hook).

                    .if HookFlag==FALSE
                        invoke InstallHook,hDlg
                        .if eax!=NULL
                            mov HookFlag,TRUE
                            invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
                        .endif

Le programme maintient un 'Flag' nommé HookFlag, pour rendre compte de l'état du hook. S'il est 'FALSE'(faux) alors c'est que le hook n'est pas activé. Au contraire s'il est TRUE(vrai) c'est qu'il est actif.
Quand l'utilisateur appuie sur le bouton 'hook', le programme vérifie s'il est déjà installé (actif). Si ce n'est pas le cas, on appelle la fonction InstallHook du hook du DLL pour l'installer. Notez que nous passons l'handle de notre dialogBox principale en tant que paramètre de la fonction pour que le DLL (contenant la fonction d'hook) puisse envoyer les différents messages WM_MOUSEHOOK vers la bonne fenêtre, c'est-à-dire la nôtre.
A partir du moment où le programme est chargé, le DLL est chargé lui aussi. En réalité, les DLLs sont immédiatement chargés après que le programme ait été mis en mémoire. L'entrypoint du DLL est appelé avant même que la première instruction du programme principal ne soit exécuté. (EntryPoint=première ligne de code lue dans un programme=début) Ainsi quand le programme principal se met en route, le(s) DLL(s) est/sont déjà initialisé(s). Nous écrivons le code suivant pour l'entrypoint du DLL :

    .if reason==DLL_PROCESS_ATTACH
        push hInst
        pop hInstance
    .endif

Ce code sauvegarde juste l'instance handle du DLL dans une variable globale nommée hInstance pour pouvoir l'utiliser dans la fonction InstallHook. Puisque l'entrypoint du DLL est appelé avant les autres fonctions dans le DLL, l'hInstance est toujours valable. On définit hInstance dans la section .data pour que cette valeur soit prête "pou le processus". Le DLL est 'mappé'(configuré) dans le process, avant même que le curseur de souris ne plane au-dessus des fenêtres ou objets qu'elle doit hooker. Imaginez-vous que par hasard il y ait déjà un DLL qui occupe l'adresse de chargement destinée au DLL (contenant notre hook), alors dans ce cas le DLL serait reconfiguré à une autre adresse. La valeur de 'hInstance' serait remise à jour en prenant en compte la nouvelle adresse de chargement. Lorsque l'utilisateur presse le bouton 'Unhook' puis par la suite le bouton 'Hook', SetWindowsHookEx sera de nouveau appelé. Cependant, cette fois, il utilisera la nouvelle adresse de chargement en tant qu' instance handle mais qui malheureusement sera faux puisque dans notre programme d'exemple, l'adresse de chargement du DLL n'aura pas été changée. Le Hook sera local, c'est pourquoi vous pourrez uniquement hooker les événements de la souris qui arriveront dans votre propre fenêtre. À peine désirable.

InstallHook proc hwnd:DWORD
    push hwnd
    pop hWnd
    invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
    mov hHook,eax
    ret
InstallHook endp

La fonction InstallHook est très simple en elle-même. Elle sauvegarde l'handle de la fenêtre dans une variable globale nommée hWnd pour l'utiliser plus tard. On appelle alors SetWindowsHookEx pour installer (activer) le hook sous la souris. La valeur de retour de SetWindowsHookEx est stockée dans une variable globale nommée hHook pour l'utiliser avec UnhookWindowsHookEx au moment où on souhaite désactiver ce hook.
Après qu'on ait appelé SetWindowsHookEx, le hook sous la souris est fonctionnel. Chaque fois qu'un événement souris arrive dans le système, MouseProc (votre procédure d'hook) est appelé.

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
    invoke CallNextHookEx,hHook,nCode,wParam,lParam
    mov edx,lParam
    assume edx:PTR MOUSEHOOKSTRUCT
    invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
    invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
    assume edx:nothing
    xor eax,eax
    ret
MouseProc endp

La première chose à faire, c'est d'appeler CallNextHookEx pour donner à d'autres Hooks la chance de traiter de nouveaux événements souris. Après ça, on appelle la fonction WindowFromPoint pour retrouver l'handle de la fenêtre (ou de l'objet) auxquelles sont indiquées les coordonnée (écran) de la souris. Remarquez que nous employons POINT dans la structure MOUSEHOOKSTRUCT représenté par lParam en tant que coordonnées actuelles de la souris. Après ça nous envoyons l'handle de la fenêtre (ou de l'objet) au programme principal via PostMessage avec le message WM_MOUSEHOOK. Une chose à se rappeler, c'est que : vous ne devez pas employer SendMessage à l'intérieur de la procédure d'hook, ceci peut provoquer une impasse pour les messages. Je recommande PostMessage. La structure MOUSEHOOKSTRUCT est définie ainsi:

MOUSEHOOKSTRUCT STRUCT DWORD
  pt            POINT <>
  hwnd          DWORD      ?
  wHitTestCode  DWORD      ?
  dwExtraInfo   DWORD      ?
MOUSEHOOKSTRUCT ENDS
 

  • 'pt' représente les coordonnées (coordonnées écran) actuelles du curseur de la souris.
  • hwnd est l'handle de la fenêtre qui recevra le message souris. En général, c'est la fenêtre qui se trouve sous le curseur de la souris, mais pas toujours. Si une fenêtre appelle SetCapture, l'entrée souris sera redirigée spécialement vers cette fenêtre au lieu de celle sur laquelle passe notre souris. À cause de ça, je n'utilise pas le membre hwnd de cette structure, mais par contre j'appelle WindowFromPoint à la place.
  • wHitTestCode représente une valeur de coup d'essai. La valeur de coup d'essai donne plus d'information sur la position actuelle du curseur de la souris. Il indique dans quelle partie de la fenêtre est ce que le curseur souris se trouve. Pour la liste complète, vérifiez votre référence api win32 avec le message WM_NCHITTEST.
  • dwExtraInfo contient l'information supplémentaire associée au message. Normalement cette valeur est créée à partir de l'appel mouse_event et retrouvée en appelant GetMessageExtraInfo.
Quand la fenêtre principale reçoit le message WM_MOUSEHOOK, on emploie l'handle de la fenêtre placé dans wParam pour retrouver l'information de la fenêtre.

    .elseif uMsg==WM_MOUSEHOOK
        invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
        invoke wsprintf,addr buffer,addr template,wParam
        invoke lstrcmpi,addr buffer,addr buffer1
        .if eax!=0
            invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
        .endif
        invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
        invoke GetClassName,wParam,addr buffer,128
        invoke lstrcmpi,addr buffer,addr buffer1
        .if eax!=0
            invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
        .endif
        invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
        invoke GetClassLong,wParam,GCL_WNDPROC
        invoke wsprintf,addr buffer,addr template,eax
        invoke lstrcmpi,addr buffer,addr buffer1
        .if eax!=0
            invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
        .endif

Pour éviter des scintillements, nous vérifions le texte qui est déjà inscrit dans les commandes d'édition avec le texte que nous souhaitons placer à leur place. s'ils sont identiques, on saute par dessus l'instruction de réécriture.
Nous retrouvons le nom de classe (la Class Name) en appelant GetClassName, et l'adresse de la procédure de fenêtre (La Window Proc) en appelant GetClassLong avec GCL_WNDPROC puis les mettons ensuite sous forme de chaînes de caractères pour les placer dans les contrôles d'édition appropriés.

                        invoke UninstallHook
                        invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
                        mov HookFlag,FALSE
                        invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
                        invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
                        invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL

Quand l'utilisateur appuie sur le bouton 'Unhook', le programme appelle la fonction UninstallHook qui se trouve dans le DLL (avec la fonction hook). UninstallHook appelle uniquement UnhookWindowsHookEx. Après ça, le texte du bouton se retransforme en "Hook", le flaf HookFlag est remis à 'False' et on bloque le contenu des contrôles d'édition.
Notez comment est décrit le linker dans le makefile.

        Link /SECTION:.bss,S  /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS

La section .bss est définie comme une section partagée pour faire en sorte que plusieurs processus (programmes) puissent utiliser les données non initialisée de ce DLL en même temps. Sans ce commutateur, votre DLL ne fonctionnerait pas correctement.


[Iczelion's Win32 Assembly Homepage]


Traduit pas Morgatte