MFC message map and command delivery

Source: Internet
Author: User
Tags function prototype message queue

Digression: When I first started learning Windows programming, I printed a detailed description of the Windows message, which lists the meanings and functions of various defined messages, a total of more than 10 pages, which are sometimes found to be useful when programming. I find a lot of programming friends, although every day to face the news, but very little attention to it. C + + programmers have a common problem, they want to write "their own" program, that is, every line of code want to write their own. If you use some libraries, you always want to fully understand what the class or function in the library is all about, otherwise it is "not practical". For the message, many friends only care about the usual few, indifferent to the rest. In fact, there are a lot of infrequently used messages in windows that are useful, and programmers may be able to implement more streamlined programming by responding to these messages. When it comes to news, in MFC, "the most familiar mystery" is a message map, which is what we are going to face when we first touch MFC. A friend with SDK programming experience turned to MFC programming, suddenly feel like everything changed. In particular, the window message and the processing of the message compared with the previous, it is irrelevant. How does a document respond to a command message if it is not a window? The first time with MFC programming, we will only use MFC ClassWizard for us to do a lot of things, the main thing is to add message response. In memory, if it is self-added message response, how cautious should we be to Begin_message_map () ... End_message_map () should be worshipped as a deity. It is a magic box, put our mantra into the right place, there will be magical power, misplaced, their program even "life" is not. It is said that knowing too much is not necessarily a good thing. I have also been planning to not understand the mystery of the area, I think when programming to know what they want to do on the line. MFC gives us something on the outside, intuitively, not only gave me a shell of a program, but also give us a lot of convenience. Microsoft's starting point may be to hope to achieve "fool programming" results, imagine, who will not use ClassWizard? As you know, Windows is message-based, with ClassWizard, you add classes, you add messages, and what you learn seems to be learning the end of your head. So many programmers think that "we do not need to go the way of the SDK, directly with MFC programming, the new things are usually simple, intuitive, easy to learn ..." When you really want to use MFC programming, you will find that the light will classwizard how foolish you are. MFC is not a common class library, ordinary class library We can not understand the details inside, as long as we know what these libraries can do, interface parameters are all right. such as the string class, the order of operations is to define a string object, and then modify the property to invoke the method. But for MFC, you're not writing a "#include MFC.h" in your program, and then it's in yourMFC class libraries are used in the program. MFC is a sugar-coated cow bone. It's easy to write a single document window, print a "I love mfc!" in the middle of the window, and then the nightmare begins ... Want to escape, intend never to understand MFC insider? There's no door! In this dark and mysterious hole of MFC, even if you are going to touch a stone, you are doomed to find an outlet. To MFC this cow bone, Microsoft gently and democratically tell you "of course you can choose not to chew it, cough ... But you must starve to death! "Message mapping and command delivery reflect the differences between MFC and the SDK." In SDK programming, there is no concept of message mapping, it has a clear callback function, through a switch statement to determine what kind of message received, and then processing the message. So, in SDK programming, it is almost possible to write the SDK program by sending a message and processing the message in the callback function. In MFC, it appears that sending messages and processing messages is simpler and more straightforward than the SDK, but unfortunately not intuitive. To give a simple example, if we want to customize a message, the SDK is very simple and intuitive, with a single statement: SendMessage (hwnd,message/ a number greater than or equal to Wm_user /,wparam,lparam), which can then be processed in the callback function. But MFC is different, because you usually do not go directly to rewrite the window's callback function, so you can only move against the original MFC code, put the message to the right place. This is indeed the same painful labor. Understanding the MFC message map principle is not an easy thing to do. We can think backwards and imagine what the message map does for us. MFC gives us a lot of convenience in automation, for example, all MFC Windows Use the same window procedure, that is, all MFC windows have a default window procedure. It's not like in SDK programming to write a window procedure for each window class. For message mapping, the most straightforward conjecture is that a message map is a data structure that concatenates "messages" with "response message function names." In this way, when the window-aware message occurs, the structure lookup is found and the corresponding message response function is executed. In fact, this idea can not be simply implemented: each of our different MFC window classes, the same message, there are different responses. That is, different MFC windows have different message response functions for the same message. At this time, we also think of a feasible method. When we design the window base class (CWND), we let it have a message response to each different message and define the message response function as an empty function. Thus, the window class derived from CWnd has an empty response to all messages, and we can overload the message response function in response to a specific message. But the result of this, a almost nothing to do the CWnd class to have hundreds of "redundant" function, that fear that these message response functions are pure virtual functions, each CWnd object also bear a huge virtual table, which is not worth the candle. Many friends have no breakthrough in learning the message map, because it was initially assumed that the purpose of MFC's message mapping was to replace the SDK window process--which was not understood correctly. But they also have a layer of understanding, that since it is a substitute for "old" things, then the MFC message body should be a higher level of abstraction, simpler, easier to understand. But the result is that if we do not pass the ClassWizard tool, manually adding the message is a pretty confusing thing. Therefore, when we learn the MFC message map, we must first understand: The purpose of the message map is not to be faster to add code to the window process, but a mechanism change. If you do not want to change the window procedure function, then where should be the message response? Many friends smattering that: we can use hook technology to grab messages in front of the message queue to crawl, the message response to the outside of the window process. Furthermore, different windows will have different messages of interest, so each MFC window should have a table linking the messages of interest with the corresponding message response functions. And then come to--the message mapping mechanism executes the steps: When a message occurs, we use hook technology to send this message to the window process captured, and then against the MFC window of the message map table, if there is a message inside the table, execute its corresponding function. Of course, with hook technology, we can theoretically complete the message response without changing the window procedure function. MFC does do this, but the actual operation may be quite different from your imagination. Now let's write the message map, we'll define a structure that has at least two items: one is the message ID, and the other is the function that responds to the message. As follows: struct Afx_msgmap_entry {UINT nmessage;//messages of interest afx_pmsg PFN;//function pointer in response to the above message} of course, only two members of the structure are connected to the message map table is immature. Windows messages are classified into standard messages, control messages, and command messages, and each type of message contains hundreds of different IDs, different meanings, and different parameters of the message. We have to accurately identify what kind of message has occurred and we must add a few more members. Also, for Afx_pmsg PFN, it actually equals the following declaration: void (CCmdTarget:: PFN) (); (Hint: afx_pmsg is a type identifier, specifically declared: typedef void (Afx_msg_call CCmdTarget:: afx_pmsg) (void);) PFN is a CCmdTarget type function pointer with no arguments and return values, and can only point to member functions in CCmdTarget class without parameters and return values, so PFN is more generic, but many of the functions we respond to messages need to pass in parameters. To resolve this contradiction, we will also add a member that represents the parameter type. Of course, there are other ... Finally, MFC our message map member structure is defined as follows: struct Afx_msgmap_entry {uint nmessage;//windows message ID uint NCode;//Notification code for control message UINT NID;//Command Message ID fan The starting value of the circumference is UINT nlastid; Command Message ID range end of the UINT NSig; The action identifier of the message afx_pmsg PFN; }; With the above message mapping table member structure, we can define an array of type afx_msgmap_entry to accommodate the message map entries. Defined as follows: Afx_msgmap_entry _messageentries[]; But that's not enough, each afx_msgmap_entry array can only hold messages of interest to the current class, which is just part of the message we want to process. For an MFC program, there are generally multiple window classes, which should have a afx_msgmap_entry array. We know that MFC also has a messaging mechanism that can send messages that it does not process to other classes for processing. To be able to find the message map for each of the MFC objects, we also add a structure to concatenate all the afx_msgmap_entry arrays together. So, we define a new struct: struct Afx_msgmap {const AFX_MSGMAP pbasemap; A Afx_msgmap object that points to a different class const afx_msgmap_entry Lpentries; A message table pointing to itself}; After that, declare a variable in each class that intends to respond to the message: Afx_msgmap Messagemap, where pbasemap points to the messagemap of the base class or another class, a one-way list of afx_msgmap elements is obtained. In this way, all message mapping information forms a message net. Of course, only the message map table is not enough, it can only be the individual MFC object messages, parameters and corresponding message response function to connect a net. To make it easier to find, MFC inserts two functions in the above class (where Theclass represents the current class): One is _getbasemessagemap (), which is used to get the base class message map function. The function is prototyped as follows: const afx_msgmap* PASCAL theclass::_getbasemessagemap ()/{return &baseClass::messageMap;}/not one is GetMessage Map (), which is used to get the function of the message map itself. The function prototype is as follows: const afx_msgmap* THECLASS::GETMESSAGEMAP () const/{return &theClass::messageMap;}/

With the message map table, we have to discuss the key to the problem, which is how the corresponding response function is called after the message has occurred. As you know, all MFC windows have an identical window process--afxwndproc (...). Here by the way to mention is, read the MFC source code friends have to, from the AfxWndProc function in, will encounter a lot of twists and puzzles, because for this huge message mapping mechanism, MFC to do a lot of things, such as optimizing messages, enhance compatibility, this a lot of work, Some even use assembly language to complete, it is difficult for us to delve into it. So we're going to omit a lot of code and analyze it rationally. For trained AfxWndProc, you can provide at most one default treatment for all messages. This is certainly not what we want. We want to finally execute the corresponding function in the message map network through AFXWNDPROC. So, what is the route of execution? From AfxWndProc down, it will eventually be called to a function onwndmsg. See Code: LRESULT CALLBACK afxwndproc (HWND hwnd,uint nmsg,wparam WPARAM, LPARAM LPARAM) {
...... cwnd* pWnd = cwnd::fromhandlepermanent (hWnd); Converts the manipulation of a handle to a CWnd object. Return Afxcallwndproc (Pwnd,hwnd,nmsg,wparam,lparam);} It is important to convert the operation of the handle to a CWnd object, because AfxWndProc is just a global function and, of course, does not know how to handle various Windows window messages, so it intelligently hands over the processing power to the MFC window object associated with the Windows window. Now large, we can almost imagine afxcallwndproc to do things, good, it has a sentence: Pwnd->windowproc (nmsg,wparam,lparam); To this, the MFC window procedure function becomes its own member function. WindowProc is a virtual function, and we can even rewrite this function to respond to different messages, which is, of course, a digression. WindowProc calls to another member function of the CWnd object onwndmsg, below to see what the approximate function of the prototype is: BOOL cwnd::onwndmsg (UINT message,wparam wparam,lparam LPARAM , lresult* PResult) {if (Message==wm_command) {OnCommand (Wparam,lparam); ...} if (message==wm_notify) {OnCommand (Wparam,lparam,&lresult); ...} Const afx_msgmap* Pmessagemap; Pmessagemap=getmessagemap (); const afx_msgmap_entry* lpentry;/The following code is used to look up the specified message from the message entry Pmessagemap with the Afxfindmessageentry function and, if found, to return a pointer to Lpentry for the member of the specified message map table. Then execute the function pointed to by the struct member's PFN/if ((Lpentry=afxfindmessageentry (pmessagemap->lpentries,message,0,0)!=null) {LPENTRY->PFN ();NOTE: This statement is not used in real MFC code. As mentioned above, different message parameters represent different meanings and different message response functions have different types of return values. PFN is a function pointer without parameters, so in real MFC code, you pass the parameter type to the message handler function according to the action identifier of the message Lpentry the object Nsig. This process involves a very complex macro substitution, as you can see here: Find a matching message and execute the corresponding function! /}}

Above, we see that onwndmsg can find matching messages and execute corresponding message responses according to the message parameters passed in. But that's not enough, we usually respond to a menu command message when the WM_COMMAND message that originally belonged to the frame window (CFrameWnd) can be placed in a View object or Document object. The principle is as follows: We see the following code in the ONWNDMSG prototype of the function above: if (Message==wm_command) {OnCommand (Wparam,lparam); That is, for the command message, it is actually given to the OnCommand function processing. The OnCommand is a virtual function, that is, when the WM_COMMAND message occurs, the MFC object corresponding to the message occurs to execute the OnCommand. For example, the Point Frame Window menu, which sends a wm_command to CFrameWnd, will result in Cframewnd::oncommand (wparam,lparam) execution. And look at the function prototype bool Cframewnd::oncommand (WPARAM Wparam,lparam LPARAM) {... return CWnd:: OnCommand (Wparam,lparam);} As you can see, it finally gives the message to CWnd:: OnCommand processing. Look again: BOOL CWnd::OnCommand (WPARAM wparam,lparam LPARAM) {... return OnCmdMsg (nid,ncode,null,null);} Here is a classic problem with C + + polymorphism. Here, although the function of the CWnd class is executed, but because this function is executed in CFrameWnd:: OnCmdMsg, that is, the current pointer is a CFrameWnd class pointer, and then ONCMDMSG is a virtual function, so if CFrameWnd is rewritten OnCommand , the program executes cframewnd::oncmdmsg (...). To Cframewnd::oncmdmsg (...) The function principle is briefly analyzed as follows: BOOL CFrameWnd:: ONCMDMSG (...) {CView PView = GetActiveView ();//Get the active visual pointer. if (pview-> OnCmdMsg (...)) return TRUE; Returned if the CView class object or its derived class object has already processed the message. ...//Otherwise, the same is done down to the document, the framework, and the application executing its own oncmdmsg. } to this, CFrameWnd:: ONCMDMThe SG completes the message response by passing the WM_COMMAND message to the View object, the Document object, and the Application object. Having written so much, we understand the approximate process of MFC message Mapping and command delivery. Now, we look at the MFC "Mysterious Code", will find much more beautiful. First look at the Declare_message_map () macro, which is defined in MFC as follows: #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; /You can see that Declare_message_map () defines two structures and a function that we are familiar with, and it is obvious that this macro provides the relevant variables and functions for each class that needs to implement the message map.

Now focus on the Begin_message_map,end_message_map and on_command three macros, which are defined in MFC as follows (where On_command and two other macros are not defined in the same file, Put it together for good looks): #define BEGIN_MESSAGE_MAP (Theclass, BaseClass)/const afx_msgmap* theclass::getmessagemap () const/{ Return &theClass::messageMap; }/afx_comdat afx_datadef const afx_msgmap theclass::messagemap =/{&baseclass::messagemap, &theClass::_ Messageentries[0]}; /afx_comdat const Afx_msgmap_entry theclass::_messageentries[] =/{/

#define ON_COMMAND (ID, memberfxn)/{wm_command, Cn_command, (word) ID, (word) ID, AFXSIG_VV, (afx_pmsg) &memberfxn},

#define END_MESSAGE_MAP ()/{0, 0, 0, 0, Afxsig_end, (afx_pmsg) 0}/}; /

Looking at three macros at a glance is a bit complicated, but it's just complicated, and the formula substitution is not difficult. And look at the following example, suppose we have a menu item in the frame as "Test", which defines the following macro: Begin_message_map (CMainFrame, CFrameWnd) on_command (id_test, OnTest) end_ Message_map ()

Then the macro expands to get the following code: Const afx_msgmap* CMAINFRAME::GETMESSAGEMAP () const{return &CMainFrame::messageMap;} The following fills in the message table mapping information const AFX_MSGMAP CMAINFRAME::MESSAGEMAP =
{&cframewnd::messagemap, &cmainframe::_messageentries[0]};//fill in the following message that holds the current class of interest and can be populated with multiple AFX_MSGMAP_ ENTRY Object Const Afx_msgmap_entry cmainframe::_messageentries[] ={{wm_command, Cn_command, (word) id_test, (word) id_test, AFXSIG_VV, (afx_pmsg) &ontest},//Join the id_test message parameter {0, 0, 0, 0, Afxsig_end, (afx_pmsg) 0}//end item of the message map for this class};

As you know, to complete the id_test message map, you also define and implement the OnTest function. This is where you want to write afx_msg void OnTest () in the header file and implement it in the source file. Based on what we learned above, we know that when the command message with ID id_test occurs, it will eventually execute to the OnTest function we wrote.

Ext.: http://blog.csdn.net/liyi268/article/details/623391

http://121.40.201.188/topic/556d27b72f4bf6bf2b92477e

MFC message map and command delivery

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.