I. Introduction▲
Dans ce chapitre nous allons aborder un message particulier : WM_COMMAND. Ce message sert à envoyer des commandes. Une commande est envoyée suite à l'action d'un contrôle par l'utilisateur. C'est-à-dire par exemple le clic d'un bouton ou d'une option d'un menu… Nous devons donc l'intercepter pour implémenter l'action que nous voulons faire. Dans l'exemple nous créerons un menu qui va nous permettre d'envoyer des commandes utilisateur aux fenêtres de l'application.
Nous verrons aussi qu'un contrôle peut envoyer des informations à l'application avec ce message quand des événements propres à lui-même se produisent. On les appelle des notifications.
Nous aborderons aussi l'envoi de messages par programme.
II. Création du menu▲
Pour préparer notre exemple, nous allons d'abord créer le menu. Il existe deux solutions pour créer des menus, soit par programme, soit à l'aide de ressources. Les ressources feront l'objet d'un autre chapitre. Nous allons donc utiliser la première solution (à savoir qu'il est plus courant d'utiliser les ressources pour construire un menu).
#define IDM_QUIT 1
HMENU hMenu, hSousMenu;
hSousMenu =
CreateMenu
(
);
AppendMenu
(
hSousMenu, MF_STRING, IDM_QUIT, "
Quitter
"
);
hMenu =
CreateMenu
(
);
AppendMenu
(
hMenu,MF_POPUP,(
UINT)hSousMenu,"
Fichier
"
);
Les menus sont créés avec la fonction CreateMenu qui renvoie un handle de menu (HMENU). Dans l'exemple nous créons un menu principal et un sous-menu. Nous créons d'abord le sous-menu, car il devra être ajouté au menu principal. Nous lui ajoutons une option de menu à l'aide de la fonction AppendMenu. Nous pouvons utiliser cette fonction autant de fois que l'on a d'options à rajouter. Elle reçoit comme paramètres :
le handle de menu auquel on veut l'ajouter.
Une constante indiquant son aspect (MF_STRING, car c'est une chaîne de caractères).
L'identificateur de l'option qui la distinguera parmi les autres. C'est une constante de type UINT (unsigned int) que l'on peut nommer pour plus de clarté dans le code (#define IDM_QUIT 1).
Le dernier paramètre est un pointeur sur la chaîne de caractères représentant l'option visuellement dans le menu ("Quitter" dans l'exemple).
Nous créons ensuite le menu principal de la même façon. Nous lui ajoutons le sous-menu créé précédemment toujours avec la fonction AppendMenu en lui passant le handle du sous-menu comme paramètre (troisième paramètre de AppendMenu). On lui donnera un aspect de type popup (deuxième paramètre de AppendMenu à MF_POPUP).
Maintenant, il faut l'ajouter à la fenêtre principale de l'application. Dans l'exemple nous le ferons lors de la création de la fenêtre en passant son handle comme paramètre à la fonction
hwnd =
CreateWindow
(
"
MaWinClass
"
, "
Titre
"
, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400
, 300
,
NULL
, hMenu, hinstance, NULL
);
III. Les commandes▲
Comment savoir que l'utilisateur a actionné l'option du menu ?
Et bien en traitant le message WM_COMMAND. Comme nous l'avons dit plus haut, ce message est envoyé par Windows quand une commande utilisateur a été actionnée.
Mais comment savoir que cette commande vient d'une option particulière d'un menu ?
Dans le mot de poids faible du paramètre wParam qui est envoyé avec le message WM_COMMAND : LOWORD(wParam). Il contient l'identificateur que l'on avait attribué à l'option de menu lors de son ajout au menu (IDM_QUIT dans l'exemple) :
switch
(
uMsg)
{
case
WM_COMMAND :
if
(
LOWORD
(
wParam) ==
IDM_QUIT) PostMessage
(
hwnd, WM_CLOSE,0
,0
);
return
0
;
Il suffit alors de faire un test sur ce paramètre si l'on est en train de traiter un message WM_COMMAND et d'agir en conséquence.
IV. Envoyer des messages▲
Dans cet exemple si l'on a affaire à la commande IDM_QUIT nous demanderons à la fenêtre de se fermer en lui envoyant un message WM_CLOSE avec la fonction PostMessage. Eh oui, nous aussi nous pouvons envoyer des messages !
PostMessage permet de déposer des messages dans la file d'attente.
Son premier paramètre est le handle de la fenêtre de destination.
Son second paramètre est l'identificateur du message (WM_CLOSE dans l'exemple).
Les deux paramètres suivants sont les valeurs des champs wParam et lParam du message (wParam et lParam sont inutilisés pour le message WM_CLOSE).
Il existe une autre fonction pour envoyer des messages : SendMessage. Elle reçoit les mêmes paramètres que PostMessage et dans le même ordre. La différence est qu'elle envoie le message directement à la procédure de fenêtre sans passer par la file d'attente. Ensuite, elle attend que le message ait été traité, afin de retourner un résultat. Dans l'exemple nous l'utilisons pour ordonner au contrôle d'édition de changer sa police de caractères :
HFONT hFont;
hFont =
(
HFONT)GetStockObject
(
ANSI_FIXED_FONT);
SendMessage
(
hEdit,WM_SETFONT,(
UINT)hFont,TRUE);
Nous avons ici récupéré le handle d'une police de caractères prédéfinie de Windows avec la fonction GetStockObject (cette fonction permet de récupérer des objets initialisés au démarrage de Windows, tels des pinceaux, crayons...).
Nous allons lui envoyer un second message pour mettre ses marges intérieures droite et gauche à 5 pixels :
SendMessage
(
hEdit, EM_SETMARGINS, EC_LEFTMARGIN |
EC_RIGHTMARGIN,
MAKELONG
(
5
, 5
));
La valeur des marges est envoyée dans un seul paramètre. Il est reconstitué en un DWORD avec la macro MAKELONG. Vous avez dû remarquer que le nom de ce message est préfixé des lettres EM au lieu de WM. C'est tout simplement qu'il s'agit d'un message destiné spécifiquement aux contrôles d'édition. EM comme Edit Message et bien sûr WM comme Window Message qui eux s'adressent à tous les types de fenêtres.
V. Les notifications▲
Pendant le déroulement du programme, les contrôles envoient des messages WM_COMMAND à leur fenêtre parent. Ceci quand ils subissent certains événements. Prenons comme exemple le contrôle d'édition, à chaque fois que vous modifiez son texte, il envoie un message WM_COMMAND avec le paramètre EN_CHANGE, pour nous le notifier. On les appelle les notifications. Elles sont envoyées dans le mot de poids fort du champ wParam : HIWORD(wParam) du message WM_COMMAND. Je vous recommande de consulter l'aide API Win32 pour chaque contrôle, afin vous documenter sur leurs différentes notifications.
switch
(
uMsg)
{
case
WM_COMMAND :
if
(
HIWORD
(
wParam) ==
EN_CHANGE) EditNotChg =
FALSE;
return
0
;
Dans l'exemple, à la réception de cette notification nous mémoriserons dans une variable ce changement. Variable que nous testerons à la fermeture de la fenêtre principale afin de proposer une boîte de message pour laisser le choix à l'utilisateur de fermer ou non la fenêtre s'il y a eu modification du texte du contrôle d'édition :
switch
(
uMsg)
{
case
WM_CLOSE :
if
(
EditNotChg ||
MessageBox
(
hwnd,"
Le texte a été modifié.
\r\n
Êtes-vous sûr de \
vouloir fermer l'application ?
"
,"
Question ?
"
,MB_YESNO |
MB_ICONQUESTION ) ==
IDYES)
DestroyWindow
(
hwnd);
return
0
;
Nous interceptons pour cela le message WM_CLOSE. Nous testons la variable EditNotChg si elle est à TRUE, DestroyWindow est appelé, ce qui a pour conséquence d'envoyer un message WM_DESTROY et donc de fermer l'application. Si EditNotChg est à FALSE la fonction MessageBox est appelée si elle retourne IDYES, nous appelons de même DestroyWindow, sinon le programme continue.
MessageBox, vous l'avez compris sert à appeler des boîtes de message. Son premier paramètre est le handle de fenêtre de son parent. Le deuxième paramètre est le texte du message ou de la question. Le troisième paramètre est son titre. Le quatrième est une constante indiquant son type. (MB_YESNO | MB_ICONQUESTION) dans l'exemple, car elle possède un bouton "Oui", un bouton "Non" et l'icône de question (voir l'aide API Win32 pour les autres possibilités).
VI. Code complet▲
#include <windows.h>
#define IDM_QUIT 1
HINSTANCE hinst;
LRESULT CALLBACK MainWndProc
(
HWND, UINT, WPARAM, LPARAM);
int
WINAPI WinMain
(
HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int
nCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wc;
HMENU hMenu, hSousMenu;
hinst =
hinstance;
wc.style =
0
;
wc.lpfnWndProc =
MainWndProc;
wc.cbClsExtra =
0
;
wc.cbWndExtra =
0
;
wc.hInstance =
hinstance;
wc.hIcon =
NULL
;
wc.hCursor =
LoadCursor
(
NULL
, IDC_ARROW);
wc.hbrBackground =
NULL
;
wc.lpszMenuName =
NULL
;
wc.lpszClassName =
"
MaWinClass
"
;
if
(!
RegisterClass
(&
wc)) return
FALSE;
hSousMenu =
CreateMenu
(
);
AppendMenu
(
hSousMenu, MF_STRING, IDM_QUIT, "
Quitter
"
);
hMenu =
CreateMenu
(
);
AppendMenu
(
hMenu,MF_POPUP,(
UINT)hSousMenu,"
Fichier
"
);
hwnd =
CreateWindow
(
"
MaWinClass
"
, "
Titre
"
, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400
, 300
,
NULL
, hMenu, hinstance, NULL
);
if
(!
hwnd) return
FALSE;
ShowWindow
(
hwnd, nCmdShow);
while
(
GetMessage
(&
msg, NULL
, 0
, 0
))
{
TranslateMessage
(&
msg);
DispatchMessage
(&
msg);
}
return
msg.wParam;
}
/**
***************************************************************************
*/
LRESULT CALLBACK MainWndProc
(
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static
HWND hEdit;
static
BOOL EditNotChg =
TRUE;
switch
(
uMsg)
{
case
WM_CREATE:
{
HFONT hFont;
hEdit =
CreateWindow
(
"
edit
"
, "
Texte
"
,
WS_CHILD |
WS_VISIBLE |
ES_MULTILINE |
ES_WANTRETURN |
WS_VSCROLL,
0
, 0
, 0
, 0
, hwnd, NULL
, hinst, NULL
);
hFont =
(
HFONT)GetStockObject
(
ANSI_FIXED_FONT);
SendMessage
(
hEdit,WM_SETFONT,(
UINT)hFont,TRUE);
SendMessage
(
hEdit, EM_SETMARGINS, EC_LEFTMARGIN |
EC_RIGHTMARGIN,
MAKELONG
(
5
, 5
));
return
0
;
}
case
WM_CLOSE :
if
(
EditNotChg ||
MessageBox
(
hwnd,"
Le texte a été modifié.
\r\n
Êtes-vous sûr de \
vouloir fermer l'application ?
"
,"
Question ?
"
,MB_YESNO |
MB_ICONQUESTION ) ==
IDYES)
DestroyWindow
(
hwnd);
return
0
;
case
WM_COMMAND :
if
(
LOWORD
(
wParam) ==
IDM_QUIT) PostMessage
(
hwnd, WM_CLOSE,0
,0
);
if
(
HIWORD
(
wParam) ==
EN_CHANGE) EditNotChg =
FALSE;
return
0
;
case
WM_SIZE :
MoveWindow
(
hEdit, 0
, 0
, LOWORD
(
lParam), HIWORD
(
lParam), TRUE);
return
0
;
case
WM_DESTROY :
PostQuitMessage
(
0
);
return
0
;
default
:
return
DefWindowProc
(
hwnd, uMsg, wParam, lParam);
}
}
Comme vous pouvez le constater, les messages sont omniprésents dans Windows.
Ce code a été testé sous VC++, CodeBlocks(Mingw), C++ Builder, DevC++.
À vos PC.
CGi