TN006 : tables des messages
Cette note décrit l’installation de la carte de messages MFC.
Le problème
Microsoft Windows implémente des fonctions virtuelles dans des classes de fenêtre qui utilisent sa fonctionnalité de messagerie. En raison du grand nombre de messages impliqués, la fourniture d’une fonction virtuelle distincte pour chaque message Windows créerait une table virtuelle de grande taille prohibitive.
Étant donné que le nombre de messages Windows définis par le système change au fil du temps et que les applications peuvent définir leurs propres messages Windows, les mappages de messages fournissent un niveau d’indirection qui empêche les modifications de l’interface de rompre le code existant.
Vue d’ensemble
MFC offre une alternative à l’instruction switch utilisée dans les programmes Windows traditionnels pour gérer les messages envoyés à une fenêtre. Un mappage entre les messages et les méthodes peut être défini afin que lorsqu’un message soit reçu par une fenêtre, la méthode appropriée est appelée automatiquement. Cette fonctionnalité de mappage de messages est conçue pour ressembler à des fonctions virtuelles, mais présente des avantages supplémentaires qui ne sont pas possibles avec les fonctions virtuelles C++.
Définition d’un mappage de messages
La macro DECLARE_MESSAGE_MAP déclare trois membres pour une classe.
Tableau privé d’entrées AFX_MSGMAP_ENTRY appelées _messageEntries.
Structure AFX_MSGMAP protégée appelée messageMap qui pointe vers le tableau _messageEntries .
Fonction virtuelle protégée appelée
GetMessageMap
qui retourne l’adresse de messageMap.
Cette macro doit être placée dans la déclaration de n’importe quelle classe à l’aide de mappages de messages. Par convention, il se trouve à la fin de la déclaration de classe. Par exemple :
class CMyWnd : public CMyParentWndClass
{
// my stuff...
protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Il s’agit du format généré par AppWizard et ClassWizard lorsqu’ils créent des classes. Les crochets ///{{ et //}} sont nécessaires pour ClassWizard.
La table du mappage de messages est définie à l’aide d’un ensemble de macros qui s’étendent aux entrées de mappage de messages. Une table commence par un appel de macro BEGIN_MESSAGE_MAP , qui définit la classe gérée par ce mappage de messages et la classe parente à laquelle les messages non gérés sont passés. La table se termine par l’appel de macro END_MESSAGE_MAP .
Entre ces deux appels de macro, il s’agit d’une entrée pour que chaque message soit géré par ce mappage de messages. Chaque message Windows standard a une macro du formulaire ON_WM_MESSAGE_NAME qui génère une entrée pour ce message.
Une signature de fonction standard a été définie pour décompresser les paramètres de chaque message Windows et fournir une sécurité de type. Ces signatures se trouvent dans le fichier Afxwin.h dans la déclaration de CWnd. Chacun d’eux est marqué avec le mot clé afx_msg pour faciliter l’identification.
Remarque
ClassWizard exige que vous utilisiez les afx_msg mot clé dans vos déclarations de gestionnaire de mappage de messages.
Ces signatures de fonction ont été dérivées à l’aide d’une convention simple. Le nom de la fonction commence toujours par "On
« ». Ceci est suivi du nom du message Windows avec le « WM_ » supprimé et la première lettre de chaque mot en majuscules. L’ordre des paramètres est wParam suivi de LOWORD
(lParam) puis HIWORD
(lParam). Les paramètres inutilisés ne sont pas passés. Tous les handles encapsulés par les classes MFC sont convertis en pointeurs vers les objets MFC appropriés. L’exemple suivant montre comment gérer le message WM_PAINT et provoquer l’appel de la CMyWnd::OnPaint
fonction :
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
La table de mappage de messages doit être définie en dehors de l’étendue d’une fonction ou d’une définition de classe. Il ne doit pas être placé dans un bloc « C » extern.
Remarque
ClassWizard modifie les entrées de mappage de messages qui se produisent entre le crochet de commentaire ///{{ et //}}.
Messages Windows définis par l’utilisateur
Les messages définis par l’utilisateur peuvent être inclus dans un mappage de messages à l’aide de la macro ON_MESSAGE . Cette macro accepte un numéro de message et une méthode du formulaire :
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
Dans cet exemple, nous établissons un gestionnaire pour un message personnalisé qui a un ID de message Windows dérivé de la base de WM_USER standard pour les messages définis par l’utilisateur. L’exemple suivant montre comment appeler ce gestionnaire :
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
La plage de messages définis par l’utilisateur qui utilisent cette approche doit se trouver dans la plage WM_USER pour 0x7fff.
Remarque
ClassWizard ne prend pas en charge l’entrée de routines de gestionnaire ON_MESSAGE à partir de l’interface utilisateur ClassWizard. Vous devez les entrer manuellement à partir de l’éditeur Visual C++. ClassWizard analyse ces entrées et vous permet de les parcourir comme n’importe quelle autre entrée de mappage de messages.
Messages Windows inscrits
La fonction RegisterWindowMessage est utilisée pour définir un nouveau message de fenêtre qui est garanti être unique dans tout le système. La macro ON_REGISTERED_MESSAGE est utilisée pour gérer ces messages. Cette macro accepte le nom d’une variable UINT NEAR qui contient l’ID de message Windows inscrit. Par exemple
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
La variable d’ID de message Windows inscrite (WM_FIND dans cet exemple) doit être une variable NEAR en raison de la façon dont ON_REGISTERED_MESSAGE est implémentée.
La plage de messages définis par l’utilisateur qui utilisent cette approche se trouvera dans la plage 0xC000 pour 0xFFFF.
Remarque
ClassWizard ne prend pas en charge la saisie de routines de gestionnaire ON_REGISTERED_MESSAGE à partir de l’interface utilisateur ClassWizard. Vous devez les entrer manuellement à partir de l’éditeur de texte. ClassWizard analyse ces entrées et vous permet de les parcourir comme n’importe quelle autre entrée de mappage de messages.
Messages de commande
Les messages de commande à partir de menus et d’accélérateurs sont gérés dans les mappages de messages avec la macro ON_COMMAND. Cette macro accepte un ID de commande et une méthode. Seul le message WM_COMMAND spécifique qui a un wParam égal à l’ID de commande spécifié est géré par la méthode spécifiée dans l’entrée de carte de message. Les fonctions membres du gestionnaire de commandes ne prennent pas de paramètres et ne retournent void
pas. La macro a la forme suivante :
ON_COMMAND(id, memberFxn)
Les messages de mise à jour de commande sont routés via le même mécanisme, mais utilisent plutôt la macro ON_UPDATE_COMMAND_UI. Les fonctions membres du gestionnaire de mise à jour de commande prennent un paramètre unique, un pointeur vers un objet CCmdUI et retournent void
. La macro a le formulaire
ON_UPDATE_COMMAND_UI(id, memberFxn)
Les utilisateurs avancés peuvent utiliser la macro ON_COMMAND_EX, qui est une forme étendue de gestionnaires de messages de commande. La macro fournit un sur-ensemble de la fonctionnalité ON_COMMAND. Les fonctions membres du gestionnaire de commandes étendues prennent un paramètre unique, un UINT qui contient l’ID de commande et retournent un boOL. La valeur de retour doit être TRUE pour indiquer que la commande a été gérée. Sinon, le routage continuera vers d’autres objets cibles de commande.
Exemples de ces formulaires :
À l’intérieur de Resource.h (généralement généré par Visual C++)
#define ID_MYCMD 100 #define ID_COMPLEX 101
À l’intérieur de la déclaration de classe
afx_msg void OnMyCommand(); afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI); afx_msg BOOL OnComplexCommand(UINT nID);
À l’intérieur de la définition de carte de messages
ON_COMMAND(ID_MYCMD, OnMyCommand) ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand) ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
Dans le fichier d’implémentation
void CMyClass::OnMyCommand() { // handle the command } void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI) { // set the UI state with pCmdUI } BOOL CMyClass::OnComplexCommand(UINT nID) { // handle the command return TRUE; }
Les utilisateurs avancés peuvent gérer une plage de commandes à l’aide d’un seul gestionnaire de commandes : ON_COMMAND_RANGE ou ON_COMMAND_RANGE_EX. Pour plus d’informations sur ces macros, consultez la documentation produit.
Remarque
ClassWizard prend en charge la création de gestionnaires ON_COMMAND et de ON_UPDATE_COMMAND_UI, mais elle ne prend pas en charge la création de gestionnaires ON_COMMAND_EX ou de ON_COMMAND_RANGE. Toutefois, l’Assistant Classe analyse et vous permet de parcourir les quatre variantes de gestionnaire de commandes.
Messages de notification de contrôle
Les messages envoyés à partir de contrôles enfants vers une fenêtre ont un bit supplémentaire d’informations dans leur entrée de carte de messages : l’ID du contrôle. Le gestionnaire de messages spécifié dans une entrée de carte de messages est appelé uniquement si les conditions suivantes sont remplies :
Le code de notification de contrôle (mot élevé de lParam), tel que BN_CLICKED, correspond au code de notification spécifié dans l’entrée de carte de message.
L’ID de contrôle (wParam) correspond à l’ID de contrôle spécifié dans l’entrée de mappage de message.
Les messages de notification de contrôle personnalisé peuvent utiliser la macro ON_CONTROL pour définir une entrée de carte de messages avec un code de notification personnalisé. Cette macro a le formulaire
ON_CONTROL(wNotificationCode, id, memberFxn)
Pour une utilisation avancée ON_CONTROL_RANGE pouvez être utilisé pour gérer une notification de contrôle spécifique à partir d’une plage de contrôles avec le même gestionnaire.
Remarque
ClassWizard ne prend pas en charge la création d’un gestionnaire de ON_CONTROL ou de ON_CONTROL_RANGE dans l’interface utilisateur. Vous devez les entrer manuellement avec l’éditeur de texte. ClassWizard analyse ces entrées et vous permet de les parcourir comme n’importe quelle autre entrée de mappage de messages.
Les contrôles communs Windows utilisent les WM_NOTIFY les plus puissants pour les notifications de contrôle complexes. Cette version de MFC prend directement en charge ce nouveau message à l’aide des macros ON_NOTIFY et ON_NOTIFY_RANGE. Pour plus d’informations sur ces macros, consultez la documentation produit.