Gestion des messages
Nous avons vu dans le chapitre de création de fenêtres filles, que les fenêtres (filles et parents) devaient impérativement communiquer entre elles. La solution passera par l'utilisation des files de messages de chacunes des fenêtres qui récupéreront les messages dans leur procédure de gestions des messages.
Envoyer des messages
Pour envoyer un message, il existe deux fonctions:
La fonction SendMessage():
Envoyer un message sur une fenêtre, avec attente du traitement de ce message par la cible (Equivaut à avoir un accusé réception).
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
Paramètres
hWnd : Handle d'une fenêtre, si le paramètre est HWND_BROADCAST,
le message sera envoyé à toutes les fenêtres (y compris
les fenêtres cachées ou désactivées, ...) se trouvant
directement sur le bureau (mais les fenêtres filles de celles-ci ne le
recevront pas).
Msg: Numéro du message à envoyer (WM_CLOSE,
WM_USER, ...)
wParam : information complémentaire
lParam : information complémentaire
(Ne pas confondre:Si vous connaissez déjà les threads, vous n'utiliserez jamais cette fonction pour envoyer un message à un thread, il faudra utiliser PostThreadMessage...).
Pour les boîtes de dialogues qui vont envoyer des messages à leurs contrôles enfants, vous utiliserez une fonction spécialisée. En effet, lorsque vous dessinez votre template de dialogue-box, vous allez utiliser des identificateurs sur des contrôles et non pas des handles sur les contrôles.
Cette fonction va attendre le traitement du message avant de rendre la main.
LRESULT SendDlgItemMessage(
HWND hDlg,
int nIDDlgItem,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
Paramètres
hDlg : handle de la dialogue qui veut communiquer avec son contrôle
enfant.
nIDDlgItem : identifiant déclaré dans le script de la dialogue
pour le contrôle enfant.
Msg : Numéro du message à envoyer (WM_...)
wParam : information complémentaire
lParam : information complémentaire
En sortie, la fonction retourne la valeur renvoyé suite au traitement du message par le contrôle enfant.
Envoyer un message sur une fenêtre, sans attente du traitement de ce message par la cible.
Les paramètres sont identiques à SendMessage().
Vous avez déjà vu une commande pour traiter les messages : GetMessage () qui est une fonction qui va lire les messages dans la file d'attente et qui, si aucun message n'est présent dans la file, va attendre bien sagement le prochain message. Avantage de cette fonction: elle rend la main au système, permettant aux autres applications d'effectuer des tâches. Inconvénient, si vous vouliez en profiter pour faire autre chose, vous ne pourrez rien faire...
C'est pourquoi, il existe la fonction PeekMessage () qui est similaire à la fonction GetMessage (). Cette fonction peut permettre d'éviter de passer par un thread. Cependant, il faudra faire attention à ne pas passer tout son temps à lire la file de message...
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
Les paramètres
lpMsg : Attention, c'est un pointeur sur une structure de type MSG qui recevra
en sortie le message qui se trouvait dans la file d'attente.
hWnd : Handle sur la fenêtre du thread courant. Si NULL, ce sera tous
messages de toutes les fenêtres du thread courant. Enfin, INVALID_HANDLE_VALUE
pour lire les messages posté avec PostThreadMessage().
wMsgFilterMin: Filtrer les messages, il correspond à l'intervalle des
valeurs des messages que l'on veut bien traiter. On stockera ici la valeur minimum
de l'intervalle.
wMsgFilterMax: Ici, on stockera la valeur maximum de l'intervallle.
Par exemple, on pourra demamnder que les messages clavier en mettant les valeurs
WM_KEYFIRST et WM_KEYLAST, ou encore de la souris en mettant WM_MOUSEFIRST et
WM_MOUSELAST
Enfin, si vous ne voulez pas de filtre, il suffit de mettre NULL comme valeurs
aux deux bornes de l'intervalle.
wRemoveMsg: Permet de regarder le message sans toucher à la file de message(PM_NOREMOVE),
ou alors de retirer le message de la file de message (PM_REMOVE). On pourra
utiliser PM_NOYIELD pour informer d'autres threads qui seraient aussi en attente.
En sortie, la valeur est nule si pas de message dans la file d'attente.
A noter, WM_QUIT est toujours retirer de la file de messages (filtre ou non) !
Les priorités de lecture des messages sont, comme indiqué chez Microsoft:
* Sent messages
* Posted messages
* Input (hardware) messages and system internal events
* Sent messages (again)
* WM_PAINT messages
* WM_TIMER messages
C'est pour cette raison que les filtres deviennent intéressant, pour traiter les messages que l'on veut en priorité !
Reprenons donc l'exemple de bouton personnalisé:
Dans l'exemple de fenêtre fille spécialisée en bouton, lorsque l'utilisateur clique sur le bouton "fermer", il serait bien que l'application se ferme. Pour cela, il faut envoyer un message à la fenêtre parent:
et toujours les images du bouton dans tous ses états :
Pour connaître le handle de la fenêtre parent, il faut utilisez la fonction GetParent():
Enfin, un début de code pour gérer une barre de menu perso contenant des boutons:
Le code est assez simplifié, puisqu'il fait une pseudo barre horizontale dans la zone cliente de notre fenêtre principale, et nous pouvons voir les boutons s'afficher sur chaques options de menus présentes dans la barre. Si on clique sur une option, le bouton s'enfonce...
En fait, il y a création d'une fenêtre principale de gestion du menu dans la fenêtre de notre application. Cette fenêtre va elle même créer autant de fenêtres filles qu'il y a d'options (10 ici). chacunes des fenêtres va ensuite afficher un pseudo bouton si on la survole (Message WM_MOUSEMOVE) et gérer un bouton enfoncé si on clique dessus impliquant une capture de la souris...
Rien que par ce petit bout de code, on constate qu'il devient difficile de gérer tous ces objets en C, et que la programmation serait grandement simplifiée en C++...
D'autres problèmes ne sont pas vus: pas d'allocation mémoire, limitant ainsi le nombre d'objets, des couleurs fixes qui risquent de ne pas être portables d'une version à une autre de Windows, ...
Enfin, je ne pousserai pas plus loin cette exemple, car évidemment, un tel objet existe déjà: Il s'agit du contrôle ToolBar de Microsoft. Mais bon, il est toujours bon de s'essayer à refaire ce genre de chose, car si vous y parvenez, c'est que vous avez déjà bien compris les principes de développement sous Windows !