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 :
Avant la Sousclassification :
Windows ==> contrôle d'édition de la procédure de fenêtre.
Après mise en place d'un système de Sousclassification :
Windows ==> notre procédure de fenêtre -----> contrôle d'édition de la procédure de fenêtre.
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.
SetWindowLong PROTO hWnd:DWORD, nIndex:DWORD, dwNewLong:DWORD
hWnd = handle de la fenêtre où l'on doit changer la valeur à l'intérieur de la structure WNDCLASSEX.
nIndex = valeur à changer.
GWL_EXSTYLE Défini un nouveau 'style étendu' de fenêtre.
GWL_STYLE Défini un nouveau style de fenêtre.
GWL_WNDPROC défini une nouvelle adresse pour la procédure de fenêtre.
GWL_HINSTANCE Défini une nouvelle application d 'instance handle'.
GWL_ID Défini un nouvel (ID) identificateur de la fenêtre.
GWL_USERDATA impose de travailler avec des valeurs de 32 bits pour cette fenêtre. Chaque fenêtre a une valeur correspondante à 32 bits destinée à être utilisée par l'application qui a créé cette fenêtre.
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.
CallWindowProc PROTO lpPrevWndFunc:DWORD, \
hWnd:DWORD,\
Msg:DWORD,\
wParam:DWORD,\
lParam:DWORD
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:
invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
mov OldWndProc,eax
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.
.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
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.
.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
.end
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