Tutorial 20: Les Sousclassifications de Fenêtre
(Window Subclassing)

Dans ce Tutorial, nous allons voir les éléments de sousclassifications d'une fenêtre, ce que c'est et comment les employer à votre avantage.
Downloadez l'exemple ici.

Théorie:

Si ça fait un petit moment que vous programmez sous Windows, vous avez dû rencontrer certains cas où une fenêtre possède pratiquement les attributs (les caractéristiques) dont vous auriez besoin pour votre programme, mais pas tout à fait quand même. Vous avez sûrement déjà dû rencontrer cette situation où il vous faut un contrôle d'édition un peu spécial qui puisse filtrer certains types de textes indésirables ! La meilleur chose à faire, c'est de coder votre propre fenêtre. Mais c'est le travail vraiment difficile et très long. Voilà donc à notre secours les 'Sousclassifications' de fenêtre.
Grâce aux sousclassifications de fenêtre on peut "reprendre" (corriger améliorer) une fenêtre sousclassée. Vous y aurez un contrôle absolu. On va prendre un exemple pour que ce soit plus clair. Supposons que vous avez besoin d'une boîte de texte qui n'accepte que des nombres hexadécimaux. Si vous utilisez un simple contrôle d'édition, rien n'interdit à l'utilisateur de taper autre chose que des nombres hexadécimaux. Si l'utilisateur tape "zb+q *" dans la boîte de texte, vous ne pouvez rien en faire sauf rejeter la totalité de cette chaîne de caractères. C'est pas très professionnel. En réalité, vous avez besoin de pouvoir examiner chaque caractère tapé par l'utilisateur au moment même où il le tape dans la boîte de saisie.
Nous allons examiner comment faire çà. Quand l'utilisateur tape quelque chose dans une boîte de texte, Windows envoie le message WM_CHAR à la procédure de fenêtre qui s'occupe de la gestion du contrôle d'édition. Cette procédure de fenêtre réside à l'intérieur de Windows lui-même donc nous ne pouvons rien modifier. Cependant nous pouvons faire suivre le flux de message de notre propre procédure de fenêtre pour que notre procédure de fenêtre devienne la première (avant que Windows s'en occupe) à traiter n'importe quel message envoyé au contrôle d'édition. Si on veut faire réagir notre procédure de fenêtre à un éventuel message (si on veut détecter des caractères non désirés), on doit le faire ainsi. Mais si elle ne veut pas prendre en compte le message, on peut le passer à la procédure de fenêtre originale. De cette manière, notre procédure de fenêtre s'insère entre Windows et le contrôle d'édition. Voyez plutôt le flux ci-dessous : Maintenant concentrons notre attention sur le fait de savoir comment sous-classer une fenêtre. Remarquez que la sousclassification n'est pas uniquement limitée aux simples 'controls' (commandes), on peut aussi l'employée avec n'importe quelle fenêtre.
Réfléchissons ! Comment Windows sait à quel endroit est-ce que le contrôle d'édition (de la procédure de fenêtre) réside. Vous avez devinez ? ...... regardez le membre lpfnWndProc de la structure WNDCLASSEX. Si nous pouvons remplacer ce membre par l'adresse de notre propre procédure de fenêtre, alors Windows enverra des messages à notre proc de fenêtre au lieu de l'autre.
On peut faire ça en appelant SetWindowLong. hWnd = handle de la fenêtre où l'on doit changer la valeur à l'intérieur de la structure WNDCLASSEX.
nIndex = valeur à changer. dwNewLong = C'est la valeur de substitution.
Donc notre travail est facile : Nous codons (écrivons) une proc de fenêtre qui manipulera les messages du contrôle d'édition et appellera ensuite SetWindowLong avec flag renvoyé par GWL_WNDPROC, en faisant passer l'adresse de notre proc de fenêtre comme troisième paramètre. Si la fonction réussit, la valeur de retour est la valeur vue précédemment (l'entier 32 bits), dans notre cas, c'est l'adresse de la procédure de fenêtre originale. Nous avons besoin de stocker cette valeur pour pouvoir ensuite l'utiliser dans notre propre procédure de fenêtre.
Rappelez-vous qu'il y a certains messages (ici des chiffres ou des lettres non-hexadécimales) que nous ne souhaitons pas récupérer, donc nous les passerons à la procédure de fenêtre originale. Nous pouvons faire ça en appelant la fonction CallWindowProc. LpPrevWndFunc = adresse de la 'procédure de fenêtre' originale.
Ci-dessus, ces quatre paramètres sont ceux passés à notre procédure de fenêtre. On les passe uniquement grâce à CallWindowProc.

Echantillon de Code :

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
ClassName db "SubclassWinClass",0
AppName    db "Subclassing Demo",0
EditClass db "EDIT",0
Message db "You pressed Enter in the text box!",0

.data?
hInstance HINSTANCE ?
hwndEdit dd ?
OldWndProc dd ?

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_APPWORKSPACE
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
 WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
           CW_USEDEFAULT,350,200,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    .while TRUE
        invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .endw
    mov eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg==WM_CREATE
        invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClass,NULL,\
            WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
            20,300,25,hWnd,NULL,\
            hInstance,NULL
        mov hwndEdit,eax
        invoke SetFocus,eax
        ;-----------------------------------------
        ; On le sous-classe !
        ;-----------------------------------------
        invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
        mov OldWndProc,eax
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp

EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    .if uMsg==WM_CHAR
        mov eax,wParam
        .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
            .if al>="a" && al<="f"
                sub al,20h
            .endif
            invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
            ret
        .endif
    .elseif uMsg==WM_KEYDOWN
        mov eax,wParam
        .if al==VK_RETURN
            invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SetFocus,hEdit
        .else
            invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
            ret
        .endif
    .else
        invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
EditWndProc endp
end start

Analyse:

Après que le contrôle d'édition soit créé, nous le sous-classons en appelant SetWindowLong, en remplaçant l'adresse de la procédure de fenêtre originale par notre propre procédure de fenêtre. Remarquez que nous stockons l'adresse de la procédure de fenêtre originale pour pouvoir l'utiliser plus tard avec CallWindowProc. Notez qu' EditWndProc est une procédure de fenêtre ordinaire. Grâce à EditWndProc, nous filtrons les messages WM_CHAR. Si le caractère est compris entre 0-9 ou A-F, nous l'acceptons en le laissant arriver jusqu'à la procédure de fenêtre originale. Si c'est une lettre comprise entre A-F, nous la convertissons en lettre entre a-f en ajoutant 20h à la valeur hexadécimale qui représente cette lettre. Notez bien que, si le caractère n'est pas celui nous attendons, nous le rejetons. Nous ne le passons pas à la procédure de fenêtre originale. Donc lorsque l'utilisateur tape quelque chose d'autre que 0-9 ou a-f, le caractère n'apparaît pas dans le contrôle d'édition. Je vous démontre en plus (dans le programme) la puissance de la méthode de sous-classification avec la détection de la touche Entre. EditWndProc vérifie si la valeur du message WM_KEYDOWN est VK_RETURN (la Touche Enter). Si c'est le cas, une boîte de message affiche s'ouvre pour afficher : "You pressed the Enter key in the text box!" (Vous avez appuyé sur la touche Enter dans la boîte de texte!). Si ce n'est pas la touche Enter, on passe le message à la procédure de fenêtre originale.
Vous pouvez employer la méthode de sous-classification de fenêtre pour prendre le contrôle d'autres fenêtres. C'est une des corde que vous devez avoir à votre arc.

[Iczelion's Win32 Assembly Homepage]


Traduit par Morgatte