• notice
  • Congratulations on the launch of the Sought Tech site

MFC detailed introduction-the realization of message mapping

MFC explain in simple terms-the realization of message mapping

Implementation of message mapping

 

 

  1. Windows message overview

     

    The input of the Windows application is sent by the Windows system to the window of the application in the form of a message. These windows receive and process messages through window procedures, and then return control to Windows.

     

     

    1. Classification of messages

       

 

  1. Queued messages and non-queued messages

     

    From the perspective of the way the message is sent, there are two types of messages: queue messages and non-queue messages. Queue messages are sent to the system message queue, and then to the thread message queue; non-queue messages are sent directly to the destination window procedure.

    Here, the message queue is elaborated as follows:

    Windows maintains a system message queue, and each GUI thread has a thread message queue.

     

    Mouse and keyboard events are converted into input messages by the mouse or keyboard driver and put them into the system message queue, such as WM_MOUSEMOVE, WM_LBUTTONUP, WM_KEYDOWN, WM_CHAR, etc. Every time Windows removes a message from the system message queue, it determines which window it is sent to and which thread created the window, and then puts it into the thread message queue of the window creation thread. The thread message queue receives messages sent to the window created by the thread. The thread fetches the message from the message queue and sends it to the appropriate window procedure through Windows for processing.

     

    In addition to keyboard and mouse messages, queue messages also include WM_PAINT, WM_TIMER and WM_QUIT.

     

    The vast majority of messages other than these queue messages are non-queue messages.

     

  2. System messages and application messages

     

From the perspective of the source of the message, it can be divided into: system-defined messages and application-defined messages.

The range of system message ID is from 0 to WM_USER-1, or 0X80000 to 0XBFFFF; application messages are from WM_USER (0X0400) to 0X7FFF, or 0XC000 to 0XFFFF; messages from WM_USER to 0X7FFF are used by the application itself; from 0XC000 to 0XFFFF The message is used to communicate with other applications.For the uniqueness of the ID, use ::RegisterWindowMessage to get the message ID of the range.

 


    1. Message structure and message processing

       


 

  1. The structure of the message

     

    In order to obtain message information from the message queue, the MSG structure needs to be used. For example, the ::GetMessage function (getting the message from the message queue and removing it from the queue) and the ::PeekMessage function (getting the message from the message queue but not removing it) both use this structure to save the obtained message information.

    The MSG structure is defined as follows:

     

    typedef struct tagMSG {// msg

    HWND hwnd;

     

    UINT message;

    WPARAM wParam;

    LPARAM lParam;

    DWORD time;

    POINT pt;

    } MSG;

     

    The structure includes six members to describe the relevant attributes of the message:

    The handle of the window receiving the message, the message identifier (ID), the first message parameter, the second message parameter, the time when the message was generated, and the mouse position when the message was generated.

     

     

  2. The application processes the message through the window procedure

     

    As mentioned earlier, each "window class" must register a window procedure of the following form:

    LRESULT CALLBACK MainWndProc (

    HWND hwnd,// window handle

     

    UINT msg,// message identification

     

    WPARAM wParam, // message parameter 1

     

    LPARAM lParam// message parameter 2

     

    )

    The application processes messages through window procedures: non-queue messages are sent directly to the window procedure of the target window by Windows, and queue messages are dispatched to the window procedure of the target window by :: DispatchMessage, etc. When the window procedure is called, it accepts four parameters:

     

    a window handle (window handle);

     

    a message identifier (message identifier);

     

    two 32-bit values called message parameters (two 32-bit message parameters);

     

    If necessary, the window procedure uses ::GetMessageTime to get the time when the message was generated, and ::GetMessagePos to get the position of the mouse cursor when the message was generated.

     

    In window procedures, use switch/case branch processing statements to identify and process messages.

     

     

  3. The application obtains the processing of the message through the message loop

     

    After each GDI application is created in the main window, it will enter the message loop to accept user input, interpret and process messages.

    The structure of the message loop is as follows:

    while (GetMessage(&msg, (HWND) NULL, 0, 0)) {// Get the message from the message queue

     

    if (hwndDlgModeless == (HWND) NULL ||

    !IsDialogMessage(hwndDlgModeless, &msg) &&

    !TranslateAccelerator(hwndMain, haccel, &msg)) {

    TranslateMessage(&msg);

    DispatchMessage(&msg); // Send message

     

    }

     

    }

    The message loop gets the message from the message queue.If it is not a shortcut key message or a dialog box message, the message conversion and dispatch are performed, and the window procedure of the destination window handles it.

    When getting the message WM_QUIT, or ::GetMessage error, exit the message loop.

     

     

  4. MFC message processing

     

When programming with the MFC framework, the essence of message sending and processing is also as described above. However, one thing that needs to be emphasized is that all MFC windows use the same window procedure, and programmers do not need to design and implement their own window procedures, but process messages through a set of message mapping mechanisms provided by MFC. Therefore, MFC simplifies the complexity of processing messages when programmers are programming.

The so-called message mapping, to put it simply, is to let the programmer specify a certain MFC class (a class with message processing capabilities) to process a certain message. MFC provides a tool ClassWizard to help implement message mapping, and add some content about message mapping and member functions for processing messages in the message processing class. The programmer will complete the message processing function to achieve the desired message processing capability.

If the derived class wants to cover the message processing function of the base class, use ClassWizard to add a message mapping entry in the derived class, define a function with the same prototype, and then implement the function. This function overrides the same-named processing function of any base class of the derived class.

 

The following sections will analyze the realization principle of MFC's message mechanism and the process of message processing. To this end, we must first analyze the inside story of ClassWizard's realization of message mapping, and then discuss the MFC window procedure, and analyze how the MFC window procedure implements message processing.

 


    1. Three types of messages processed by MFC

       

    2. Definition and implementation of message mapping

       

       

According to different processing functions and processes, MFC mainly processes three types of messages:

 

 

  • For Windows messages, the prefix starts with "WM_", except for WM_COMMAND. Windows messages are directly sent to the MFC window procedure for processing, and the window procedure calls the corresponding message processing function. Generally, this type of message is handled by the window object, that is to say, this type of message processing function is generally a member function of the MFC window class.

     

     

  • The control notification message is a WM_COMMAND notification message sent by the control child window to the parent window. The window procedure calls the corresponding message processing function. Generally, this type of message is handled by the window object, that is to say, this type of message processing function is generally a member function of the MFC window class.

     

It should be pointed out that Win32 uses the new WM_NOFITY to handle complex notification messages. The WM_COMMAND type notification message can only pass a control window handle (lparam), control window ID and notification code (wparam). WM_NOTIFY can transmit arbitrarily complex information.

 

  • Command messages, which are WM_COMMAND notification messages from user interface objects such as menus, toolbar buttons, accelerator keys, etc., are messages defined by the application. Through the message mapping mechanism, the MFC framework distributes commands to multiple types of objects (with message processing capabilities) in a certain path, such as documents, windows, applications, document templates and other objects. The class that can handle message mapping must be derived from the CCmdTarget class.

     

After discussing the classification of messages, it should be time to discuss how to handle various messages. However, to know how to handle messages, you must first know how to map messages.

 


    1. The realization method of MFC message mapping

       

      MFC uses ClassWizard to help implement message mapping.It adds some message mapping content to the source code, and declares and implements message processing functions. Now let's analyze these added content.

       

      In the class definition (header file), it adds a message processing function declaration, and adds a line of macro DECLARE_MESSAGE_MAP that declares the message map.

       

      In the implementation of the class (implementation file), implement the message processing function, and use the IMPLEMENT_MESSAGE_MAP macro to implement message mapping. Under normal circumstances, these declarations and implementations are automatically maintained by MFC's ClassWizard. Look at an example:

       

      In the source code of the application class generated by AppWizard, the definition (header file) of the application class contains code similar to the following:

       

      //{{AFX_MSG(CTttApp)

      afx_msg void OnAppAbout();

      //}}AFX_MSG

      DECLARE_MESSAGE_MAP()

       

      The implementation file of the application class contains code similar to the following:

      BEGIN_MESSAGE_MAP(CTApp, CWinApp)

      //{{AFX_MSG_MAP(CTttApp)

      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)

      //}}AFX_MSG_MAP

      END_MESSAGE_MAP()

       

      The header file is the declaration of message mapping and message processing function, and the implementation file is the realization of message mapping and message processing function. It means let the application object process the command message ID_APP_ABOUT, and the message processing function is OnAppAbout.

       

      Why is a message map completed after doing this? What exactly did these declarations and implementations do? Next, these issues will be discussed.

       

    2. Inside the declaration and implementation

       


 

  1. DECLARE_MESSAGE_MAP macro:

     

    First, look at the contents of the DECLARE_MESSAGE_MAP macro:

    #ifdef _AFXDLL

    #define DECLARE_MESSAGE_MAP () "

    private: "

    static const AFX_MSGMAP_ENTRY _messageEntries[]; "

    protected: "

    static AFX_DATA const AFX_MSGMAP messageMap; "

    static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); "

    virtual const AFX_MSGMAP* GetMessageMap() const; "

     

    #else

    #define DECLARE_MESSAGE_MAP () "

    private: "

    static const AFX_MSGMAP_ENTRY _messageEntries[]; "

    protected: "

    static AFX_DATA const AFX_MSGMAP messageMap; "

    virtual const AFX_MSGMAP* GetMessageMap() const; "

     

    #endif

    DECLARE_MESSAGE_MAP defines two versions for static or dynamic linking to the MFC DLL.

     

     

  2. BEGIN_MESSAE_MAP macro

     

    Then, look at the content of the BEGIN_MESSAE_MAP macro:

    #ifdef _AFXDLL

    #define BEGIN_MESSAGE_MAP(theClass, baseClass) "

    const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() "

    {return &baseClass::messageMap;} "

    const AFX_MSGMAP* theClass::GetMessageMap() const "

    {return &theClass::messageMap;} "

    AFX_DATADEF const AFX_MSGMAP theClass::messageMap = "

    {&theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; "

    const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = "

    {"

     

    #else

    #define BEGIN_MESSAGE_MAP(theClass, baseClass) "

    const AFX_MSGMAP* theClass::GetMessageMap() const "

    {return &theClass::messageMap;} "

    AFX_DATADEF const AFX_MSGMAP theClass::messageMap = "

    {&baseClass::messageMap, &theClass::_messageEntries[0] }; "

    const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = "

    {"

     

    #endif

     

    #define END_MESSAGE_MAP() "

    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0} "

    }; "

    Correspondingly, BEGIN_MESSAGE_MAP defines two versions for static or dynamic linking to the MFC DLL. END_MESSAGE_MAP is relatively simple, with only one definition.

     

     

  3. ON_COMMAND macro

     

Finally, look at the content of the ON_COMMAND macro:

#define ON_COMMAND(id, memberFxn) "

{"

WM_COMMAND,"

CN_COMMAND,"

(WORD)id,"

(WORD)id,"

AfxSig_vv,"

(AFX_PMSG)memberFxn"

};

 



    1. Explanation of message mapping declaration

       


After clarifying the definitions of macros, let’s analyze their functions and functions.

The essence of the message map declaration is to add several static member variables and static or virtual functions to the class in which they are located.Of course, they are variables and functions related to the message map.

 

  1. Member variables

     

Two member variables are added, the first is _messageEntries, and the second is messageMap.

 

 

  • The declaration of the first member variable:

     

AFX_MSGMAP_ENTRY _messageEntries[]

This is an array variable of type AFX_MSGMAP_ENTRY , which is a static member variable used to hold the message map entry of the class. A message map entry can be described by the AFX_MSGMAP_ENTRY structure.

 

The definition of the AFX_MSGMAP_ENTRY structure is as follows:

 

struct AFX_MSGMAP_ENTRY

{

//Windows message ID

 

UINT nMessage;

// Notification code of control message

 

UINT nCode;

//ID of Windows Control

 

UINT nID;

// If a certain range of messages is mapped, then nLastID specifies its range

 

UINT nLastID;

 

UINT nSig;// The action ID of the message

 

// The function that should be executed in response to the message (routine to call (or special value))

 

AFX_PMSG pfn;

};

It can be seen from the above structure that each mapping has two parts: the first part is about the message ID, including the first four fields; the second part is about the execution function corresponding to the message, including the last two fields.

 

In the six fields of the above structure, pfn is a pointer to the CCmdTarger member function. The type of function pointer is defined as follows:

typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);

When the message map array is initialized with one or more message map entries, various message functions of different types are converted into such types: they do not receive parameters and do not return the types of parameters. Because all classes that can have message maps are derived from CCmdTarge, this conversion can be achieved.

 

nSig is an identification variable used to identify message processing functions of different prototypes.Each message processing function of different prototypes corresponds to a different nSig. During message distribution, MFC internally dispatches the message to the corresponding member function for processing according to nSig.In fact, it restores the pfn to the corresponding type of message processing function and executes it according to the value of nSig.

 

 

 

  • Declaration of the second member variable

     

AFX_MSGMAP messageMap;

This is a static member variable of type AFX_MSGMAP, which can be guessed from its type name and variable name.It is a variable that contains message mapping information. Indeed, it packs the message map information (message map array) and related functions together, that is to say, when you get the variable of a message processing class, you get all its message map data and functions. The definition of AFX_MSGMAP structure is as follows:

 

struct AFX_MSGMAP

{

// Get the data or function of the message map entry address of the base class

 

#ifdef _AFXDLL

//pfnGetBaseMap points to the _GetBaseMessageMap function

 

const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();

#else

//pBaseMap saves the address of the base class message map entry _messageEntries

 

const AFX_MSGMAP* pBaseMap;

#endif

//lpEntries saves the address of the message mapping entry _messageEntries

 

const AFX_MSGMAP_ENTRY* lpEntries;

};

As can be seen from the above definition, the address of the message map array _messageEntries and function _GetBaseMessageMap of the class can be obtained through messageMap (when the MFC DLL is not used, it is the address of the base class message map array).

 

 

 

  1. Member function

     

 

  • _GetBaseMessageMap()

     

The function used to get the base class message map.

 

 

  • GetMessageMap()

     

The function used to get its own message map.

 



    1. Explanation of message mapping implementation

       


The essence of message mapping is to initialize the static member functions _messageEntries and messageMap defined in the declaration, and realize the declared static or virtual functions GetMessageMap and _GetBaseMessageMap.

 

In this way, before entering the WinMain function, each MFC class that can respond to a message generates a message mapping table, and the program is run to determine whether it needs to respond to a message by querying the table.

 

 

  1. Initialization of the message map entry table (message map array)

     

    As mentioned earlier, the elements of the message map array are message map entries, and the format of the entries conforms to the description of the structure AFX_MESSAGE_ENTRY. Therefore, to initialize the message map array, it must be filled with data that conforms to the format: if the current class is specified to process a message, the information (four) related to the message and the address and prototype of the message processing function are combined into A message map entry is added to the message map array.

     

    Obviously, this is a tedious job. In order to simplify the operation, MFC divides the message mapping into several categories according to the different messages and the different message processing methods.Each type of message mapping has at least one commonality: the prototype of the message processing function is the same. For each type of message mapping, MFC defines a macro to simplify the work of initializing the message array. For example, the ON_COMMAND macro mentioned above is used to map the command message, as long as the command ID and message processing function are specified, because for this kind of command message mapping entry, the other four attributes are fixed. The initialization content of the ON_COMMAND macro is as follows:

     

    {WM_COMMAND,

     

    CN_COMMAND,

    (WORD)ID_APP_ABOUT,

    (WORD)ID_APP_ABOUT,

    AfxSig_vv,

    (AFX_PMSG)OnAppAbout

    }

    The meaning of this message mapping entry is: the message ID is ID_APP_ABOUT, OnAppAbout is converted to the pointer type of AFX_PMSG, AfxSig_vv is an enumeration variable predefined by MFC, used to identify the function type of OnAppAbout as parameter null (Void), return null (Void).

     

    At the end of the message map array is the content of the macro END_MESSAGE_MAP, which identifies the termination of the message map entry of the message processing class.

     

     

  2. Initialization of messageMap

     

    As mentioned earlier, the type of messageMap is AFX_MESSMAP.

     

    After initialization, the domain lpEntries saves the address of the message map array _messageEntries; if it is dynamically linked to the MFC DLL, pfnGetBaseMap saves the address of the _GetBaseMessageMap member function; otherwise, pBaseMap saves the address of the message map array of the base class.

     

     

  3. Implementation of the function

     

_GetBaseMessageMap()

It returns the member variable messagMap of the base class (when using MFC DLL), use this function to get the base class message map entry table.

 

 

GetMessageMap():

 

It returns the member variable messageMap, and uses this function to get its own message map entry table.

 

 

By the way, the base class CCmdTarget of the message mapping class also implements the above-mentioned functions related to message mapping, but its message mapping array is empty.

 

 

Since the message map macro facilitates the realization of the message map, it is necessary to discuss the message map macro in detail. The next section introduces the classification, usage and purpose of message map macros.

 


    1. Types of message map macros

       


In order to simplify the programmer's work, MFC defines a series of message mapping macros and enumeration variables like AfxSig_vv, as well as standard message processing functions, and implements these functions in detail. The message mapping macros are mainly discussed here, and the commonly used ones are divided into the following categories.

 

 

  1. A macro used for Windows messages, prefixed with "ON_WM_".

     

    Such a macro does not take parameters, because the function name and function prototype of the corresponding message and message processing function are determined. MFC provides the definition and default implementation of such message processing functions. Each such macro handles different Windows messages.

     

    For example: the macro ON_WM_CREATE() maps the message WM_CREATE to the OnCreate function.The first member nMessage of the message mapping entry is specified as the ID of the Windows message to be processed, and the second member nCode is specified as 0.

     

     

  2. Macro ON_COMMAND for command messages

     

This type of macro takes parameters, and you need to specify the command ID and message processing function through the parameters. These messages are mapped to WM_COMMAND, that is, the first member nMessage of the message mapping entry is designated as WM_COMMAND, and the second member nCode is designated as CN_COMMAND (ie 0). The prototype of the message processing function is void (void), with no parameters and no return value.

 

In addition to the mapping of a single command message, there is also a mapping macro ON_COMMAND_RANGE that maps a certain range of command messages to a message processing function. This type of macro takes parameters and needs to specify the range of the command ID and the message processing function. These messages are mapped to WM_COMMAND, that is, the first member nMessage of the message mapping entry is designated as WM_COMMAND, the second member nCode is designated as CN_COMMAND (ie 0), the third member nID and the fourth member nLastID are designated The start and end range of the mapping message. The prototype of the message processing function is void (UINT), there is a UINT type parameter, which indicates the ID of the command message to be processed, and does not return a value.

 

(3) Macros used to control notification messages

 

This type of macro may have three parameters, such as ON_CONTROL, you need to specify the control window ID, notification code and message processing function; it may also have two parameters, such as the macros ON_BN_CLICKED, ON_LBN_DBLCLK, ON_CBN_EDITCHANGE, etc., which handle specific notification messages Specify the control window ID and message processing function.

 

The control notification message is also mapped to WM_COMMAND, that is, the nMessage of the first member of the message mapping entry is designated as WM_COMMAND, but the second member nCode is a specific notification code, and the third member nID is the ID of the control child window., The fourth member nLastID is equal to the value of the third member. The prototype of the message processing function is void (void), with no parameters and no return value.

 

There is also a type of macro processing notification message ON_NOTIFY, which is similar to ON_CONTROL, but the control notification message is mapped to WM_NOTIFY. The nMessage of the first member of the message map entry is designated as WM_NOTIFY, the second member nCode is a specific notification code, the third member nID is the ID of the control child window, and the fourth member nLastID is equal to the value of the third member. The prototype of the message processing function is void (NMHDR*, LRESULT*), the parameter 1 is the NMHDR pointer, and the parameter 2 is the LRESULT pointer, which is used to return the result, but the function does not return a value.

 

Correspondingly, there is also a mapping macro that maps a certain notification message of a certain range of control sub-windows to a message processing function, such macros include ON_CONTROL_RANGE and ON_NOTIFY_RANGE. This type of macro takes parameters and needs to specify the range of the control sub-window ID and notification message, as well as the message processing function.

 

For ON__CONTROL_RANGE, the nMessage of the first member of the message mapping entry is designated as WM_COMMAND, but the second member nCode is a specific notification code, and the third member nID and fourth member nLastID are equal to the range that specifies the control window ID. The prototype of the message processing function is void (UINT).The parameter indicates which ID of the control subwindow sent the notification message to be processed.The function does not return a value.

 

For ON__NOTIFY_RANGE, the nMessage of the first member of the message mapping entry is designated as WM_NOTIFY, the second member nCode is a specific notification code, and the third member nID and fourth member nLastID specify the range of the control window ID. The prototype of the message processing function is void (UINT, NMHDR*, LRESULT*), the parameter 1 indicates which ID of the control subwindow of the notification message to be processed is sent, the parameter 2 is the NMHDR pointer, and the parameter 3 is the LRESULT pointer, which is used to return The result, but the function does not return a value.

 

(4) ON_UPDATE_COMMAND_UI macro for user interface interface status update

 

This type of macro is mapped to the message WM_COMMND, with two parameters, you need to specify the user interface object ID and message processing function. The first member nMessage of the message map entry is designated as WM_COMMAND, the second member nCode is designated as -1, and the third member nID and fourth member nLastID are both designated as the user interface object ID. The prototype of the message processing function is void (CCmdUI*), and the parameter points to a CCmdUI object and does not return a value.

 

Correspondingly, there is a macro ON_UPDATE_COMMAND_UI_RANGE to update a user interface object with a certain ID range.This macro takes three parameters to specify the user interface object ID range and message processing function. The first member nMessage of the message map entry is designated as WM_COMMAND, the second member nCode is designated as -1, and the third member nID and fourth member nLastID are used to specify the range of user interface object IDs. The prototype of the message processing function is void (CCmdUI*), the parameter points to a CCmdUI object, and the function does not return a value. The reason why the current user interface object ID is not used as a parameter is because the CCmdUI object contains relevant information.

 

(5) Macros for other messages

 

For example, ON_MESSAGE for user-defined messages. This type of macro takes parameters and needs to specify the message ID and message processing function. The first member nMessage of the message map entry is designated as the message ID, the second member nCode is designated as 0, and the third member nID and the fourth member are also 0. The prototype of message processing is LRESULT (WPARAM, LPARAM), parameter 1 and parameter 2 are message parameters wParam and lParam, and return the value of type LRESULT.

 

(6) Expand the message mapping macro

 

Many common message mapping macros have corresponding extended message mapping macros, for example: ON_COMMAND_EX corresponding to ON_COMMAND, ON_ONTIFY_EX corresponding to ON_ONTIFY, and so on. In addition to the functions of ordinary macros, expanded macros have special uses. For detailed discussion and analysis of expanded macros, see section 4.4.3.2.

 

As a summary, the following table lists these commonly used message mapping macros.

Table 4-1 Commonly used message mapping macros

 

<T

 

Message map macro

 

use

 

ON_COMMAND

 

Map the command message to the corresponding function

 

ON_CONTROL

 

Map the control notification message to the corresponding function. MFC defines more specific macros based on different control messages, so that the user does not need to specify the notification code ID when using it, such as ON_BN_CLICKED.

 

 


Tags

Technical otaku

Sought technology together

Related Topic

1 Comments

author

atorvastatin 10mg generic & lt;a href="https://lipiws.top/"& gt;atorvastatin 10mg tablet& lt;/a& gt; order lipitor 80mg

Adszyr

2024-03-08

Leave a Reply

+