La file de messages
Lecture de la file de messages
Une application doit communiquer avec Windows. Car elle ne s'est rien de ce qui se passe et ne connait pas grand chose de son environnement. En fait, c'est à Windows d'indiquer tous les événements qui se produisent sur l'application...
Pour y parvenir, Windows va lui adresser des messages. Comme il peut y avoir de nombreux messages, et qu'il faut du temps pour les traiter tous, ces messages sont stockés dans une file d'attente. Il faudra donc extraire ces messages en provenance de la file d'attente pour ensuite les interpréter:
Pour extraire les messages de la file d'attente créée par Windows, la fonction WinMain (par exemple) pourra se servir de la fonction GetMessage()
BOOL GetMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax
);
Remarque :
- Si la fonction ne voit pas de message concernant l'application, le système Windows pourra lancer le traitement d'un tout autre programme/application. Cette fonction ne va donc pas rendre la main tant qu'il n'y a pas de message pour l'application.
c'était
pratiquement la seule solution pour permettre un multitâche "correcte"
sous les Windows 16 bits.
- Le fait que la fonction GetMessage ne rende pas la main s'il n'y a pas de message peut dans certains cas poser quelques problèmes. C'est pourquoi il existe une autre fonction : PeekMessage() qui va pouvoir lire la file de messages mais qui rendra la main immédiatement à l'application message ou pas ! Mais il s'agit là d'un tout autre chapitre.
Le message extrait sera placé dans la structure de type MSG qui sera détaillé à la fin de ce chapitre...
Les messages:
Ce qu'il faut savoir pour le moment, c'est que dans cette structure, il y a le message qui est un entier et indique le type d'événement (redessiner, touche pressée, ascenseur bougé, ...). Bien entendu, il existe des constantes prédéfinies dans Windows.h. Par exemple:
WM_PAINT pour indiquer qu'il faut redessiner une partie de la fenêtre.
WM_QUIT pour indiquer que l'utilisateur veut mettre fin au programme.
Mais il y en à (beaucoup) d'autres...
Mais revenons à la fonction getMessage():
La fonction getMessage() retourne une valeur > 0 tant que le message reçu n'est pas WM_QUIT (fin du programme).
Si le message est WM_QUIT, la valeur sera à 0.
S'il y a une erreur dans la fonction getMessage (par exemple si hWnd est invalide), la valeur sera -1. Il faudra alors utiliser la fonction GetLastError pour avoir plus de détails.
Les paramètres sont :
lpMsg est
un paramètre de sortie. Les autres paramètres sont des paramètres
en entrée...
lpMsg : C'est un pointeur sur une structure de type MSG qui recevra l'adresse du message en provenance de la file des messages.
hWnd : Handle de la fenêtre sur laquel on
veut traiter les messages. Une valeur NULL va permettre de traiter les messages
de toutes les fenêtres.
wMsgFilterMin:
Spécifie la valeur la plus petite du message à extraire. Par exemple,
l'utilisation de WM_KEYFIRST va permettre d'extraire le premier message clavier
de la file (idem avec WM_MOUSEFIRST pour la souris).
wMsgFilterMax:
Spécifie la valeur la plus grande du message à extraire. Par exemple,
l'utilisation de WM_KEYLAST va permettre d'extraire le dernier message clavier
de la file (idem avec WM_MOUSELAST pour la souris).
Jamais essayé, sous Windows XP: Il faut utiliser WM_INPUT dans wMsgFilterMin et dans wMsgFilterMax pour ne recevoir que les messages WM_INPUT.
Généralement, wMsgFilterMin et wMsgFilterMax sont positionnées à zéro. GetMessage retourne alors tous les messages.
Une fois le message récupéré, reste à faire une éventuelle traduction...
En effet, il est possible d'avoir des événements du clavier qui font exactement la même chose que des événements souris (je pense aux raccourcies des menus par exemple). Il est donc plus facile de ne traiter qu'un type de message plutôt que de gérer tous les messages possibles.
C'est le rôle de TranslateMessage qui va traduire les messages concernant le clavier:
BOOL TranslateMessage(const MSG *lpMsg);
La valeur en sortie sera différente de zéro si le message est traduit. Sinon elle est à zéro. En général, cette valeur est tout simplement ignorée.
Lors de la définition d'une classe de fenêtres, il faut indiquer une procédure qui va gérer les événements concernant l'instance de fenêtre de cette classe (wndclass.lpfnWndProc). Il faut informer cette procédure qu'un message concerne sa fenêtre et qu'il doit le traiter. Pour cela, il faut utiliser la fonction LRESULT DispatchMessage( const MSG *lpmsg);
LRESULT est la valeur retournée par la procédure. Généralement, on ne traitera pas cette valeur.
Lancer le traitement du message
La fonction DispatchMessage() demande le traitement du message c'est-à-dire l'appel de la fonction WindowProc() définie dans votre classe de fenêtre. DispatchMessage() ne se termine que lorsque que WindowProc() a terminé son travail.
Exemple de code pour lire et faire traiter le message à la procédure de message:
Pour finir, voici le type de code que l'on trouvera généralement pour traiter la file de messages:
while (GetMessage( &msg, hWnd, 0, 0)) // Seul le message WM_QUIT provoquera la sortie de cette boucle
{
// Ici, on rentre dans une boucle de lecture des messages. Les événements clavier, souris sont alors traduits en message et insérés
// dans la file de messages.TranslateMessage(&msg);
DispatchMessage(&msg);
}
Comme GetMessage peut retourner une erreur, on devrait plutôt faire ceci:
BOOL bRet;
while((bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{// Traiter l'erreur
}
else
{// Ici, on rentre dans une boucle de lecture des messages. Les événements clavier, souris sont alors traduits en message et insérés
// dans la file de messages. Seul le message WM_QUIT provoquera la sortie de cette boucleTranslateMessage(&msg);
DispatchMessage(&msg);}
}
Ne pas oublier
que le message WM_QUIT est censé
indiqué le code retour que l'application doit envoyer au système
à la fin du WinMain.
Windows réorganise sa file de messages
Windows réorganise ses messages !
- D'abord parce que les messages ont des niveaux de priorités. Les deux messages les moins urgents étant WM_PAINT (pour rafraîchir le contenu de la fenêtre) et WM_TIMER (horloge permettant de lancer une procédure à intervalle de temps réguliers (où presque puisque c'est le dernier au niveau des messages urgents)).
- Ensuite, parce qu'il n'est pas nécessaire d'avoir n messages WM_PAINT dans la liste. Un seul peut suffire. WM_PAINT est un message qui indique qu'une fenêtre est partiellement altérée. Il faut donc redessiner cette zone. Si un nouveau message WM_PAINT arrive dans la liste alors que le premier n'est pas traité, Windows va remplacer ces deux messages par un seul regroupant les deux zones altérées.
Il n' aura donc plus qu'un seul message qui englobera les deux zones clients invalides.
Structure de MSG:
struct MSG
{
HWND hwnd ; //fenêtre concernée par le message
UINT message ; //identificateur du message
WPARAM wParam ; //paramètres précisant le message
LPARAM lParam ; //paramètres précisant le message
DWORD time ; //heure où le message a été placé dans
la file d'attente
POINT pt ; //emplacement de la souris lorsque le message a été
placé
} ;
Information sur
la structure POINT ici.
Les
messages sont datés. Il sera possible de récupérer cette
information avec GetMessageTime(). Elle retourne le nombre de millisecondes
qui s'est écoulé depuis l'allumage de la machine. Le message qui
sera traité sera le dernier lu avec getMessage():
LONG WINAPI GetMessageTime(void);
La variable message est un entier et indique le type d'événement (redessiner, touche pressée, ascenseur bougé, ...). Comme déjà dit, il existe des constantes prédéfinies dans Windows.h. Par exemple:
WM_CREATE : Pour indiquer que notre fenêtre vient d'être créée.
WM_PAINT : Pour indiquer qu'une zone de la fenêtre est invalide et qu'il faut donc la redessiner.
...
Cf. WndProc pour plus de détails
Le fait de connaître un événement peut ne pas suffir, par exemple, lorsque l'on appuie sur une touche, il peut être utile de connaître cette touche non ?
C'est à cela que vont servir les champs wParam et lParam.Ils complètent l'information.
C'est fini
Voilà, vous savez tout (ou presque) concernant la lecture de la file de messages. Il vous reste donc à voir le traitement du message dans la procédure de la fenêtre.