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.

Image non trouvée !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 :

Image non trouvée !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 boucle

TranslateMessage(&msg);
DispatchMessage(&msg);

}

}

Image non trouvée !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é
} ;

Image non trouvée !Information sur la structure POINT ici.

Image non trouvée !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.