Tutorial 6: Les Entrées Clavier

Nous allons voir comment un programme Windows traite les entrées clavier.

Downloadez le fichier en example ici.

Théorie:

Puisque normalement il y a seulement un clavier pour chaque PC, tous les programmes Windows en action doivent le partager entre eux. Windows est responsable de l'envoie des frappes de touches vers la bonne fenêtre qui attend ces entrées.
Bien qu'il puisse y avoir plusieurs fenêtres sur l'écran, seule une d'entre elles a temporairement la possibilité de traiter les entrées clavier. La fenêtre qui traite les entrées est la seule qui peut recevoir des frappes de touches. Vous pouvez différencier la fenêtre qui traite les entrées des autres fenêtres en regardant la barre de titre. La barre de titre de cette fenêtre est activée.
En réalité, il y a deux principaux types de messages pour le clavier, selon votre vision du clavier. Vous pouvez voir un clavier comme un ensemble de touches. Dans ce cas, si vous pressez une touche, Windows envoie le message WM_KEYDOWN à la fenêtre qui traite les entrées clavier, signifiant ainsi qu'une touche a été enfoncée. Quand vous relâchez cette touche, Windows envoie le message WM_KEYUP. Vous traitez une touche de la même manière qu'un bouton. L'autre façon de voir le clavier consiste en ce que c'est un dispositif d'entrée de caractères. Quand vous appuyez sur la touche "a", Windows envoie un message WM_CHAR à la fenêtre qui traite les entrées, disant ainsi que l'utilisateur envoie le caractère "a". En fait, Windows envoie les messages WM_KEYDOWN et WM_KEYUP à la fenêtre qui traite les entrées et ces messages seront traduis en messages WM_CHAR par l'appel de TranslateMessage. La procédure qui s'occupe du fonctionnement de votre fenêtre peut décider de traiter les trois messages ou bien seulement les messages auxquels vous vous intéressez. La plupart du temps, vous pouvez ignorer WM_KEYDOWN et WM_KEYUP puisque la fonction TranslateMessage appelle la boucle de message qui traduit les messages WM_KEYDOWN et WM_KEYUP en message WM_CHAR. Nous nous concentrerons donc uniquement sur WM_CHAR dans ce Tutorial.
 

Example:

.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib

.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
char WPARAM 20h                         ; Le caractère que le programme reçoit du clavier.
                                                  ; 20h est la valeur pour obtenir un espace.
                                                ; Ainsi rien ne sera affiché à l'écran au moment de son ouverture.

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.code
start:
   invoke GetModuleHandle, NULL
   mov    hInstance,eax
   invoke GetCommandLine
    mov CommandLine,eax

    invoke WinMain, hInstance,NULL,CommandLine, 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_WINDOW+1
   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,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
          CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
          hInst,NULL
   mov   hwnd,eax
   invoke ShowWindow, hwnd,SW_SHOWNORMAL
   invoke UpdateWindow, hwnd
   .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
   LOCAL hdc:HDC
   LOCAL ps:PAINTSTRUCT

   .IF uMsg==WM_DESTROY
       invoke PostQuitMessage,NULL
   .ELSEIF uMsg==WM_CHAR
       push wParam
       pop  char
       invoke InvalidateRect, hWnd,NULL,TRUE
   .ELSEIF uMsg==WM_PAINT
       invoke BeginPaint,hWnd, ADDR ps
       mov    hdc,eax
       invoke TextOut,hdc,0,0,ADDR char,1
       invoke EndPaint,hWnd, ADDR ps
   .ELSE
       invoke DefWindowProc,hWnd,uMsg,wParam,lParam
       ret
   .ENDIF
   xor    eax,eax
   ret
WndProc endp
end start
 

Analyse:


char WPARAM 20h                         ; Le caractère que le programme reçoit du clavier

C'est la variable qui stockera le caractère envoyé par le clavier. Puisque le caractère est envoyé dans le paramètre WPARAM (appartenant à la procédure de fenêtre). La valeur initiale de WPARAM est 20h pour afficher un 'espace' au moment où notre fenêtre régénère son secteur client la toute première fois, quand il n'y a aucune entrée clavier. Donc au commencement, nous voulons afficher 'espace'.

   .ELSEIF uMsg==WM_CHAR
       push wParam
       pop  char
       invoke InvalidateRect, hWnd,NULL,TRUE

Ceci a été rajouté à la procédure de fenêtre pour manipuler le message WM_CHAR. Il met seulement le caractère dans la variable nommée "char" et appelle ensuite InvalidateRect. InvalidateRect fait apparaître le 'rectangle invalide' dans le secteur client et force ainsi Windows à envoyer le message WM_PAINT à la procédure de fenêtre. Sa syntaxe est la suivante :

InvalidateRect proto hWnd:HWND,\
                                lpRect:DWORD,\
                                bErase:DWORD

lpRect est un indicateur sur le rectangle qui se trouve dans le secteur client que nous voulons déclarer comme étant invalide. Si ce paramètre est nul, le secteur client entier deviendra invalide.
bErase est un drapeau (un flag) indiquant à Windows s'il a besoin d'effacer le fond (background). Si ce drapeau est VRAI, alors Windows effacera le background du rectangle invalide dès que BeginPaint sera appelé.

Ainsi la stratégie que nous avons employé ici est que : nous stockons toute l'information nécessaire touchant à la peinture du secteur client et fabriquons ensuite le message WM_PAINT pour repeindre le secteur client. Bien sûr, les codes dans la section WM_PAINT doivent savoir à l'avance ce que l'on attend d'eux. On pourrait croire qu'on tourne en rond mais c'est la méthode utilisée par Windows.
En réalité nous pouvons repeindre (réactualiser) le secteur client pendant le traitement du message WM_CHAR en appelant la paire GetDC - ReleaseDC. A ce niveau, il n'y a aucun problème. Mais ça commence à être intéressant quand notre fenêtre a besoin de repeindre son secteur client. Puisque les codes qui peignent (affichent) le caractère sont dans la section WM_CHAR, la procédure de fenêtre ne sera plus capable de repeindre notre caractère sur le secteur client. Donc la ligne suivante sert à mettre toutes les données et les codes nécessaires à peinture dans WM_PAINT. Vous pouvez envoyer le message WM_PAINT n'importe où dans votre code dès que vous voulez repeindre le secteur client.

        invoke TextOut,hdc,0,0,ADDR char,1

Quand InvalidateRect est appelé, il envoie un message WM_PAINT à la procédure de fenêtre en retour. Donc les codes dans la section WM_PAINT sont appelés. Il appelle BeginPaint comme d'habitude pour obtenir l'handle du contexte de dispositif et appelle ensuite TextOut qui dessine notre caractère dans le secteur de client à x=0, y=0. Quand vous exécutez le programme et appuyez sur une touche, vous verrez que le caractère se répercute dans le coin supérieur gauche de la fenêtre. Et quand la fenêtre est rétrécie au maximum puis réagrandie de nouveau, le caractère est toujours présent puisque tous les codes et toutes les données essentielles pour repeindre sont tous réunis dans la section WM_PAINT.


[Iczelion's Win32 Assembly HomePage]


Traduit par Morgatte