III. Les messages▲
III-A. Introduction▲
Sous Windows, la communication entre une fenêtre et le système se fait par l'intermédiaire de messages. Par exemple, lorsqu'une fenêtre est redimensionnée, Windows envoie à celle-ci le message WM_SIZE pour l'informer de cet événement. Chaque fenêtre doit avoir une fonction, appelée procédure de fenêtre, que Windows appellera automatiquement chaque fois que la fenêtre a reçu un message. Afin qu'un message atteigne effectivement cette procédure de fenêtre, l'application doit explicitement envoyer le message à la procédure en question en appelant la fonction DispatchMessage.
Une procédure de fenêtre doit avoir le prototype suivant :
LRESULT CALLBACK WndProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
Bien entendu, on peut appeler la procédure de fenêtre y, Mocassin ou WindowProc au lieu de WndProc, mais en l'appelant WndProc, notre code sera plus facile à lire par d'autres programmeurs puisque ce nom est largement utilisé dans la communauté des programmeurs Windows. Parlons maintenant du rôle de chaque paramètre de cette fonction.
Un message est en fait une structure déclarée dans winuser.h comme suit :
typedef
struct
tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}
MSG;
- hwnd identifie le destinataire du message.
- message spécifie le message proprement dit (ex. : WM_KEYDOWN, WM_SIZE, etc.).
- wParam et lParam contiennent d'éventuelles informations supplémentaires concernant le message. Leur signification est donc entièrement dépendante du message.
- time spécifie quand le message a été envoyé.
- Et pt est une structure de type POINT qui contient la position du pointeur de la souris au moment où le message a été envoyé.
Lorsqu'on envoie un message à la procédure de fenêtre d'une fenêtre donnée (avec la fonction DispatchMessage), seuls les quatre premiers paramètres à savoir hwnd, message, wParam et lParam sont passés. La procédure de fenêtre étant chargée de traiter tous les messages reçus, son corps ressemble donc la plupart du temps à ceci :
LRESULT CALLBACK WndProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch
(
message)
{
case
XXXXX:
/* Traitement du message XXXXX */
break
;
case
YYYYY:
/* Traitement du message YYYYY */
break
;
default
:
return
DefWindowProc
(
hwnd, message, wParam, lParam);
}
return
0
;
}
Une procédure de fenêtre normalement constituée ne doit jamais ignorer un message (sauf si vous êtes bien conscient de ce que vous faites). Si le message ne nécessite aucun traitement particulier (c'est d'ailleurs le cas de la plupart des messages), alors elle doit renvoyer ce message au système pour que ce dernier puisse effectuer un traitement par défaut. C'est le rôle de la fonction DefWindowProc.
Certains messages placent plus d'une information utile dans le paramètre wParam ou lParam. Par exemple, dans le cas du message WM_SIZE, wParam contient un entier qui spécifie comment la fenêtre a été redimensionnée et lParam les nouvelles dimensions de la fenêtre : la nouvelle largeur dans la partie basse (ou mot de poids le plus faible) et la nouvelle hauteur dans la partie haute (ou mot de poids le plus fort). Un mot représente une valeur sur 16 bits. Les types WPARAM et LPARAM sont définis de la manière suivante :
typedef
UINT_PTR WPARAM;
typedef
LONG_PTR LPARAM;
Avec les types UINT_PTR et LONG_PTR définis comme suit :
#ifdef _WIN64
typedef
unsigned
__int64 UINT_PTR;
typedef
__int64 LONG_PTR;
#else
typedef
unsigned
int
UINT_PTR;
typedef
long
LONG_PTR;
#endif
Dans les versions 32 bits, WPARAM et LPARAM représentent donc des valeurs 32 bits.
Les macros LOWORD et HIWORD permettent d'extraire respectivement le mot haut et le mot bas d'une valeur DWORD (qui occupe 32 bits).
Comme nous pouvons le constater, les définitions des types et macros Windows peuvent varier d'une cible à une autre (32 ou 64 bits), mais c'est justement pourquoi il faut les utiliser, car ils permettent de compiler le même code pour des processeurs différents sans aucune modification.
Les paragraphes suivants présentent quelques messages assez courants.
III-B. Le message WM_CREATE▲
Envoyé lorsqu'une fenêtre a été créée, avant même que CreateWindow (ou CreateWindowEx) ne retourne. Paramètres :
- lParam : adresse d'une structure de type CREATESTRUCT qui contient les paramètres qui ont été passés à CreateWindow (ou CreateWindowEx).
III-C. Le message WM_CLOSE▲
Envoyé lorsqu'une fenêtre est sur le point d'être fermée. Ce message est envoyé par exemple lorsque l'utilisateur a cliqué sur le bouton fermer. Si on passe ce message à DefWindowProc, la fonction DestroyWindow est appelée et la fenêtre sera donc détruite.
III-D. Le message WM_DESTROY▲
Envoyé lorsqu'une fenêtre est sur le point d'être détruite. Le terme détruire signifie vraiment détruire (c'est-à-dire libérer les ressources utilisées) et pas seulement fermer (cacher).
III-E. Le message WM_SIZE▲
Envoyé lorsqu'une fenêtre a été redimensionnée. Paramètres :
- wParam : un entier qui spécifie comment la fenêtre a été redimensionnée. Par exemple :
Valeur |
Signification |
---|---|
0 |
Rien à signaler (la fenêtre a été redimensionnée …) |
SIZE_MAXIMIZED |
La fenêtre a été agrandie |
SIZE_MINIMIZED |
La fenêtre a été réduite |
SIZE_RESTORED |
La fenêtre a été restaurée |
- LOWORD(lParam) : la nouvelle largeur de la zone cliente
- HIWORD(lParam) : la nouvelle hauteur de la zone cliente
III-F. Les messages provenant du clavier▲
III-F-1. Généralités▲
Ces messages sont envoyés à une fenêtre uniquement lorsque celle-ci a le focus de l'utilisateur c'est-à-dire quand elle est active.
III-F-2. Les messages WM_KEYDOWN et WM_KEYUP▲
Envoyés respectivement lorsqu'une touche a été enfoncée ou relâchée. Les paramètres sont les suivants :
- wParam : le code (Virtual Key Code) de la touche ayant provoqué le message. Par exemple :
Code |
Touche |
---|---|
VK_TAB |
TAB |
VK_ESCAPE |
ECHAP |
VK_RETURN |
ENTREE |
VK_BACK |
BACKSPACE |
VK_DELETE |
DEL |
VK_F1 |
F1 |
VK_F2 |
F2 |
VK_UP Fleche |
'Haut' |
VK_DOWN |
Fleche 'Bas' |
VK_LEFT |
Fleche 'Gauche' |
VK_RIGHT |
Fleche 'Droite' |
'À' |
Touche A |
'B' |
Touche B |
'C' |
Touche C |
- lParam : contexte matériel (Repeat Count, Scan Code, etc.). Peu intéressant pour le moment.
Il arrive également assez souvent que l'on veuille connaître l'état d'une ou plusieurs touches auxiliaires (VK_SHIFT, VK_LSHIFT, VK_RSHIFT, VK_CONTROL, etc.) lorsqu'on traite les entrées de l'utilisateur, qu'il s'agisse d'une entrée provenant du clavier ou de la souris. Pour cela, on a la fonction :
int
GetKeyState
(
int
nVirtKey);
Qui retourne un entier indiquant l'état de la touche dont le code a été passé en argument. La signification de la valeur retournée par cette fonction est telle que :
- le bit de poids le plus faible indique si la touche est maintenue enfoncée ;
- le bit de poids le plus fort (et si vous êtes malin, vous remarquez qu'il s'agit du bit de signe !) indique si la touche est togglée (s'utilise normalement avec des touches telles que VK_CAPSLOCK, VK_NUMLOCK, VK_SCROLL, etc.).
On peut également simuler un événement clavier avec la fonction :
void
keybd_event
(
BYTE bVirtKey, BYTE bScanCode, DWORD dwFlags, DWORD dwExtraInfo);
Le paramètre dwFlags doit avoir la valeur 0 pour simuler l'enfoncement de la touche et KEYEVENTF_KEYUP pour simuler le relâchement de la touche. Il y a également une fonction, SendInput, qui est plus générique (peut simuler de nombreuses entrées et non uniquement des événements clavier) et qui permet de spécifier plus d'options.
III-F-3. Les messages WM_CHAR et WM_DEADCHAR▲
Ces messages sont générés à partir des messages WM_KEYDOWN et WM_KEYUP en réponse à un TranslateMessage. Les paramètres sont les mêmes sauf que wParam contient le code (ANSI ou Unicode selon le jeu de caractères que vous utilisez) du caractère au lieu de celui de la touche.
III-F-4. Les messages WM_SYSKEYDOWN et WM_SYSKEYUP▲
Envoyés lorsque la touche ALT (VK_MENU) a été enfoncée au moment de l'événement clavier.
III-G. Les messages provenant de la souris▲
Les messages suivants sont envoyés à la fenêtre active lorsque le pointeur de la souris se trouve à l'intérieur de sa zone cliente.
Message |
Événement |
---|---|
WM_LBUTTONDOWN |
Bouton gauche enfoncé |
WM_RBUTTONDOWN |
Bouton droit enfoncé |
WM_MBUTTONDOWN |
Bouton du milieu enfoncé |
WM_LBUTTONUP |
Bouton gauche relâché |
WM_RBUTTONUP |
Bouton droit relâché |
WM_MBUTTONUP |
Bouton du milieu relâché |
WM_LBUTTONDBLCLK |
Double-clique avec le bouton gauche |
WM_RBUTTONDBLCLK |
Double-clique avec le bouton droit |
WM_MBUTTONDBLCLK |
Double-clique avec le bouton du milieu |
WM_MOUSEMOVE |
Le curseur s'est déplacé |
Les paramètres sont les suivants :
- wParam : état des boutons et/ou de certaines touches clavier. Cette valeur peut être une ou une combinaison des valeurs (indépendantes) suivantes : MK_LBUTTON, MK_RBUTTON, MK_MBUTTON, MK_SHIFT, MK_CONTROL… ;
- LOWORD(lParam) : abscisse du pointeur de la souris ;
- HIWORD(lParam) : ordonnée du pointeur de la souris.
Les coordonnées sont calculées par rapport au coin supérieur gauche de la zone cliente.