What is a message?
The message system is very important for a Win32 program. It is the source of motivation for running programs. A message is a 32-bit value defined by the system. It uniquely defines an event and sends a notification to Windows to tell the application that something has happened. For example, if you click the mouse, change the window size, or press a key on the keyboard, Windows will send a message to the application.
The message itself is passed to the application as a record, which contains the Message Type and other information. For example, for a message generated by clicking the mouse, this record contains the coordinates of the mouse. This record type is MSG. MSG contains message information from the Windows application message queue. It declares the following in Windows:
Typedef struct tagmsg
{
Hwnd; the window handle that receives the message
Uint message; the constant identifier of the message, which is the message number.
Wparam; a 32-bit additional message. The exact meaning depends on the message value.
Lparam; a 32-bit additional message. The exact meaning depends on the message value.
DWORD time; the time when the message was created
Point pt; cursor/cursor position in the screen coordinate system when a message is created
} MSG;
Messages can be generated by the system or application. The system generates a message when an input event occurs. For example, when you press the key, move the mouse or click the control. The system also generates messages to respond to changes caused by applications. For example, the application changes the system font and changes the form size. Applications can generate messages to execute tasks in the form or communicate with windows in other applications.
What are messages?
We have provided the above comments. Do we have a clear understanding of the message structure? If not, let's try again to explain the following:
Hwnd 32-bit window handle. Windows can be any type of screen objects, because Win32 can maintain the handles of most visual objects (Windows, dialog boxes, buttons, edit boxes, etc ).
Message is used to distinguish the constant values of other messages. These constants can be pre-defined constants in Windows units or custom constants. The message identifier indicates the meaning of a message in the form of a constant name. After a message is received in the window process, the message identifier is used to determine how to process the message. For example, wm_paint tells the window that the form customer area needs to be re-painted when it is changed. A symbolic constant specifies the type of the system message. Its prefix specifies the type of the form for processing the interpreted message.
Wparam is a message-related constant value, or a handle to a window or control.
Lparam is usually a pointer to data in the memory. Since wparam, lparam, and pointer are both 32-bit, they can be converted to each other.
Message Identifier value
The system retains the message Identifier value in the 0x0000 range in 0x03ff (WM_USER-1. These values are used by system-defined messages. Applications cannot use these values for their own messages. The application uses messages from wm_user (0x0400) to 0x7fff, or 0xc000 to 0 xFFFF; Messages from wm_user to 0x7fff are used by the application itself; messages in the range from 0xc000 to 0xffff are used to communicate with other applications. By the way, we have a symbolic message value:
Wm_null --- 0x0000 empty message.
0x0001 ---- 0x0087 is mainly a window message.
0x00a0 ---- 0x00a9 non-customer zone message
0x0100 ---- 0x0108 Keyboard Message
0x0111 ---- 0x0126 menu message
0x0132 ---- 0x0138 Color Control Message
0x0200 ---- 0x020a mouse message
0x0211 ---- 0x0213 menu cyclic message
0x0220 ---- 0x0230 multi-document message
0x03e0 ---- 0x03e8 DDE message
0x0400 wm_user
0x8000 wm_app
0x0400 ---- 0x7fff application custom private message
What types of messages are there?
In fact, there are many messages in windows, but the types are not complicated. There are generally three types: Window messages, command messages, and control notification messages.
Window messages are the most common messages in the system. They are the messages used by the operating system and windows that control other windows. For example, createwindow, destroywindow, and movewindow will trigger window messages, and the messages generated by clicking the mouse we have discussed above are also window messages.
Command message, which is a special window message. It is used to process user requests sent from one window to another. For example, if you press a button, it will send a command message to the main window.
A control notification message refers to a message in which a child control in a window has something to notify the parent window. The notification message is only applicable to standard window controls such as buttons, list boxes, Combo boxes, and edit boxes, as well as windows public controls such as tree views and list views. For example, clicking or double-clicking a control, selecting some text in the control, and the scroll bar of the operation control will generate a notification message. It is similar to a command message. When the user interacts with the control window, the control notification message is sent from the control window to its main window. However, this message does not exist to process user commands, but to enable the main window to change controls, such as loading and displaying data. For example, if you press a button, the message sent to the parent window can also be considered as a control notification message. The message generated by clicking the mouse can be processed directly by the main window and then handed over to the control window for processing.
The window messages and control notification messages are processed directly or indirectly by the cwnd class derived class. Compared with window messages and control notification messages, a command message can process a wide range of objects. It can be processed by window classes, document classes, document templates, and application classes.
Because the control notification message is very important and many people use it, but the specific meaning is often confusing for beginners, I decided to list the common ones for your reference:
Button Control
The bn_clicked user clicked the button.
Bn_disable button disabled
Bn_doubleclicked double-click the button
Bn_hilite user/user highlighted button
Bn_paint button should be repainted
Bn_unhilite should be removed
Combo Control
The list box of the cbn_closeup combo box is closed.
The cbn_dblclk user double-clicked a string.
The list box of the cbn_dropdown combo box is pulled out.
The cbn_editchange user modified the text in the editing box.
The text in the cbn_editupdate edit box is about to be updated.
The cbn_errspace combo box has insufficient memory.
The cbn_killfocus combo box loses the input focus.
Cbn_selchange
Cbn_selendcancel user selection should be canceled
Cbn_selendok users' selection is legal
Cbn_setfocus combo box to get the input focus
Edit box Control
The text in the en_change edit box has been updated.
The en_errspace editing box has insufficient memory.
The en_hscroll user clicked the horizontal scroll bar.
The en_killfocus edit box is missing the input focus.
The content inserted by en_maxtext is truncated.
En_setfocus edit box to get the input focus
The text in the en_update edit box will be updated.
The en_vscroll user clicks the vertical scroll bar message meaning
List box Control
Lbn_dblclk
Lbn_errspace: insufficient memory in the list box
The lbn_killfocus list box is missing the input focus.
Lbn_selcancel selected to be canceled
Lbn_selchange selects another item
Lbn_setfocus list box to get the input focus
Queue messages and non-queue messages
Messages can be divided into two types: queue messages and non-queue messages. Message queues can be divided into system message queues and thread message queues. The system message queue is maintained by windows, and the thread message queue is maintained by each GUI thread. To avoid creating a message queue for non-Gui, no message queue exists when all threads are generated, only when the thread calls the GDI function number system for the first time to create a message queue for the thread. The queue message is sent to the system message queue and then to the thread message queue. Non-queue messages are directly sent to the destination window.
For queue messages, the most common is the mouse and keyboard-triggered messages, such as wm_mousermove, wm_char, and other messages, such as wm_paint, wm_timer, and wm_quit. When a mouse or keyboard event is triggered, the corresponding mouse or keyboard driver converts the event to a message and then delivers it to the system message queue, it is handled by the Windows system. In Windows, a message is retrieved from the system message queue at an appropriate time. Based on the MSG message structure we mentioned earlier, the message is sent to that window, then, the retrieved message is sent to the corresponding queue of the thread that creates the window. The following is the concern of the thread message queue. Windows is busy with its own tasks. When a thread sees a message in its own message queue, it extracts it from the queue and sends it to a suitable window through the operating system for processing.
Generally, the system always posts messages to the end of the message queue. This ensures that the window receives messages in the FIFO order. However, wm_paint is an exception. Multiple wm_paint messages in the same window are merged into one wm_paint message, and all invalid areas are merged into one invalid area. The purpose of merging wm_pain is to reduce the number of refresh windows.
Non-queue messages will bypass system queues and message queues and directly send messages to the window ,. The system sends a non-queue Message notification window, and the system sends a message Notification window. For example, when a user activates a window system, wm_activate, wm_setfocus, and wm_setcursor are sent. These Message notification windows are activated. Non-queue messages can also be generated when the application calls system functions. For example, when the program calls the setwindowpos system to send the wm_windowposchanged message. Some functions also send non-queue messages, such as the functions we will talk about below.
Message sending
After understanding these basic theories, we can send and receive simple messages.
There are three ways to send a message to a window: sending, sending, and broadcasting.
The functions for sending messages include sendmessage, sendmessagecallback, sendnotifymessage, and sendmessagetimeout. The functions for sending messages include postmessage, postthreadmessage, and postquitmessage. I only know broadcastsystemmessage and broadcastsystemmessageex.
The prototype of sendmessage is as follows: lresult sendmessage (hwnd, uint MSG, wparam, lparam). This function sends a message to one or more windows, it will not be returned until the message is processed. However, if the receiving window is a part of the same application, the window function of this window will be called immediately as a sub-program; if the window for receiving messages is created by another thread, the window system switches to the corresponding thread and calls the corresponding window function. This message is not put into the target application queue. The Return Value of the function is returned by the window function of the window that receives the message. The returned value depends on the message to be sent.
The postmessage prototype is as follows: bool postmessage (hwnd, uint MSG, wparam, lparam). This function places a message in the message queue of the thread that creates the hwnd window, this function will immediately control the returned message without waiting for the message to be processed. Note that if the hwnd parameter is hwnd_broadcast, the message will be sent to all overlapping windows and pop-up windows in the system, but the subwindows will not receive the message; if the hwnd parameter is null, this function is similar to setting the dwthreadid parameter to the flag of the current thread to call the postthreadmessage function.
From the two representative functions above, we can see the difference between the message sending method and the sending method: whether the sent message is processed immediately, and whether the function returns immediately. The sent message is processed immediately, and the function returns the message after processing. The sent message is not processed immediately, and is placed in an FIFO queue, it will not be processed until the application is empty, but the function will return immediately after messages are placed.
In fact, there is no big difference between the process of sending a message to a window and the process of directly calling a window. The only difference is that you can ask the operating system to intercept all sent messages, however, direct calls to the window processing process cannot be intercepted.
Messages sent in sending mode usually correspond to user input events, because these events are not very urgent and can be buffered slowly. For example, the mouse and keyboard messages will be sent, the buttons and other messages will be sent.
Broadcast messages are rarely used. The broadcastsystemmessage function prototype is as follows:
Long broadcastsystemmessage (DWORD dwflags, lpdword lpdwrecipients, uint uimessage, wparam, lparam); this function can send a message to the specified receiver, these receivers can be applications, installable drivers, network drivers, system-level device driver messages, and any combination of them. Note that if the dwflags parameter is bsf_query and at least one receiver returns broadcast_query_deny, the return value is 0. If bsf_query is not specified, the function sends the message to all receivers, and ignore the returned value.
Message receipt
There are three main functions for receiving messages: getmessage, peekmessage, and waitmessage.
The getmessage prototype is as follows: bool getmessage (lpmsg, hwnd, uint wmsgfiltermin, uint wmsgfiltermax ); this function is used to obtain messages that are related to Windows specified by the hwnd parameter and are in the range of message values given by the wmsgfiltermin and wmsgfiltermax parameters. Note that if hwnd is null, getmessage gets messages from any window that calls the function application. If both wmsgfiltermin and wmsgfiltermax are 0, then, getmessage returns all the available messages. After the function is obtained, all messages except the wm_paint message in the message queue will be deleted. As for wm_paint, all messages in the message queue will be deleted only after being processed.
Peekmessage prototype: bool peekmessage (lpmsg, hwnd, uint wmsgfiltermin, uint wmsgfiltermax, uint wremovemsg); this function is used to view the message queue of an application, if there is a message in it, put it into the structure referred to by lpmsg. However, unlike getmessage, The peekmessage function will not return it until there is a message in the queue. Similarly, if hwnd is null, peekmessage gets messages from any window that calls the function application. If hwnd =-1, then, the function only returns the message sent from the postappmessage function with the hwnd parameter null. If both wmsgfiltermin and wmsgfiltermax are 0, peekmessage returns all available messages. After the function is obtained, all messages except the wm_paint message in the message queue will be deleted. As for wm_paint, all messages in the message queue will be deleted only after being processed.
The following is a prototype of waitmessage: bool vaitmessage (). When an application has nothing to do, the function gives control to another application and suspends the application, it is not returned until a new message is put into the application queue.
Message Processing
Next, let's talk about message processing. First, let's take a look at the message pump in VC:
While (getmessage (& MSG, null, 0, 0 ))
{
If (! Translateaccelerator (msg. hwnd, hacceltable, & MSG ))
{
Translatemessage (& MSG );
Dispatchmessage (& MSG );
}
}
First, getmessage obtains a message from the message queue of the main thread of the process and copies it to the MSG structure. If there is no message in the queue, the getmessage function will not return until a message arrives. If you pass a window handle as the second parameter to getmessage, only messages in the specified window can be obtained from the queue. Getmessage can also filter messages from the message queue to only accept messages that fall within the range of the message queue. In this case, you must use getmessage/peekmessage to specify a message filter. This filter is a range of message identifiers, a form handle, or both. It is useful when an application is looking for a message in the message queue. The wm_keyfirst and wm_keylast constants are used to receive all keyboard messages. The wm_mousefirst and wm_mouselast constants are used to receive all mouse messages.
Then, translateaccelerator judges whether the message is a key message and an acceleration key message. If yes, this function converts several key messages into a callback function that passes the acceleration key message to the window. After the acceleration key is processed, the function translatemessage converts the two buttons wm_keydown and wm_keyup into a wm_char. However, note that the message wm_keydown and wm_keyup will still be passed to the callback function of the window.
After processing, the dispatchmessage function sends the message to the set callback function in the specified window of the message. If the message is wm_quit, getmessage returns 0 to exit the loop body. Applications can use postquitmessage to end their message loops. It is usually called in the wm_destroy message in the main window.
Below is a common small example to illustrate the use of this message pump:
If (: peekmessage (& MSG, m_hwnd, wm_keyfirst, wm_keylast, pm_remove ))
{
If (msg. Message = wm_keydown & msg. wparam = vk_escape )...
}
Here we accept all keyboard messages, so we use wm_keyfirst and wm_keylast as parameters. The last parameter can be pm_noremove or pm_remove, indicating whether the message information should be deleted from the message queue.
Therefore, this small code is used to determine whether the ESC key is pressed. If so, it will be processed.
Window Process
The Window Process is a function used to process all messages sent to this window. Any window class has a window process. A window of the same class uses the same window process to respond to messages. When the system sends a message to a window, it transmits the message data as a parameter. After the message arrives, it sorts the Message Type for processing. The parameters are used to differentiate different messages, the Window Process uses parameters to generate appropriate behavior.
Messages are not often ignored during a window. If the messages are not processed, the messages will be sent back to the default processing. In the window process, call defwindowproc to handle this problem. The Window Process must return a value as its message processing result. Most windows process only a small part of messages and pass other messages to the system through defwindowproc for default processing. The Window Process is shared by all windows that belong to the same class and can process messages for different windows. Let's take a look at the specific example:
Lresult callback wndproc (hwnd, uint message, wparam, lparam)
{
Int wmid, wmevent;
Paintstruct pS;
HDC;
Tchar szhello [max_loadstring];
Loadstring (hinst, ids_hello, szhello, max_loadstring );
Switch (Message)
{
Case wm_command:
Wmid = loword (wparam );
Wmevent = hiword (wparam );
// Parse the menu selections:
Switch (wmid)
{
Case idm_about:
Dialogbox (hinst, (lpctstr) idd_aboutbox, hwnd, (dlgproc) about );
Break;
Case idm_exit:
Destroywindow (hwnd );
Break;
Default:
Return defwindowproc (hwnd, message, wparam, lparam );
}
Break;
Case wm_paint:
HDC = beginpaint (hwnd, & PS );
// Todo: add any drawing code here...
Rect RT;
Getclientrect (hwnd, & RT );
Drawtext (HDC, szhello, strlen (szhello), & RT, dt_center );
Endpaint (hwnd, & PS );
Break;
Case wm_destroy:
Postquitmessage (0 );
Break;
Default:
Return defwindowproc (hwnd, message, wparam, lparam );
}
Return 0;
}
Message shunt
Generally, the window process is implemented through a switch statement, which is annoying. Is there a simpler way? Yes, that is, the message shunt. With the message shunt, we can divide the switch statement into smaller functions. Each message corresponds to a small function. The advantage of this is that it is easier to manage messages.
It is called message shunt because it can distribute any message. The following is a function:
Void msgcracker (hwnd, int ID, hwnd hwndctl, uint codenoctl)
{
Switch (ID)
{
Case id_a:
If (codenoworkflow = en_change )...
Break;
Case id_ B:
If (codenoworkflow = bn_clicked )...
Break;
....
}
}
Then let's modify the Window Process:
Lresult callback wndproc (hwnd, uint message, wparam, lparam)
{
Switch (Message)
{
Handle_msg (hwnd, wm_command, msgcracker );
Handle_msg (hwnd, wm_destroy, msgcracker );
Default:
Return defwindowproc (hwnd, message, wparam, lparam );
}
Return 0;
}
The following handle_msg macro is defined in windowsx. h:
# Define handle_msg (hwnd, MSG, FN )/
Switch (MSG): Return handle _ # MSG (hwnd), (wparam), (lparam), (FN ));
In fact, handle_wm_xxxx is a macro, for example: handle_msg (hwnd, wm_command, msgcracker); will be converted to the following definition:
# Define handle_wm_command (hwnd, wparam, lparam, FN )/
(FN) (hwnd), (INT) (loword (wparam), (hwnd) (lparam), (uint) hiword (wparam), 0l );
Now, everything is clear.
However, we found that there is also a macro in windowsx. h: forward_wm_xxxx. We will take the wm_command as an example to analyze it:
# Define forward_wm_command (hwnd, ID, hwndctl, codenoctl, FN )/
(Void) (FN) (hwnd), wm_command, makewparam (uint) (ID), (uint) (codenoworkflow), (lparam) (hwnd) (hwndctl ))
Therefore, forward_wm_xxxx re-constructs the message parameters, generates wparam & lparam, and then calls the defined function.
Well, it is also a paragraph here. Next time we will analyze the processing of messages in MFC.
Previously, we analyzed the basic theory and basic functions and usage of messages. Next, we will further discuss the implementation of Message Passing in MFC.
Implementation of MFC message processing
Looking at the various messages in MFC and the deep-rooted influence of c ++ in our minds, we may naturally think of one of the three major features of C ++: the virtual machine is used to transmit messages. However, after analysis, we can see that messages are not processed as we think. In MFC, messages are processed through a so-called message ing mechanism.
Why? I gave a detailed explanation of the cause in Visual C ++ technology insider translated by Pan aimin (version 4th. There are about 110 messages in the cwnd class, and there are other MFC classes. In this case, there are too many messages. In C ++, each derived class used in the program must have a vtable, each virtual function occupies a 4-byte entry address in vtable. In this way, for each specific type of window or control, all applications require a small kb table to support the virtual message control function.
If the window or control above can be barely implemented, what about menu command messages and button command messages? What should we do if different applications have different menus and buttons? This message ing System in the MFC Library avoids using a large vtable and can process command messages of various applications while processing common Windows messages.
To put it bluntly, the message mechanism in MFC is essentially a one-to-one correspondence table of huge messages and their processing functions, and then some program code inside the application framework that analyzes and processes this table. this avoids the cumbersome case statements used in SDK programming.
The basic class c0000target of message ing in MFC
If you want your control to be able to map messages, you must derive from the csf-target class. The csf-target class is the basis and core for MFC to process command messages. MFC designed many member functions and some member data for this class, basically to solve the message ing problem. All classes that respond to messages or events are derived from it, for example: there are many application, framework, document, view, and various control classes.
However, there are two functions in this class that are very important to message ing. One is the static member function dispatch1_msg, and the other is the virtual function on1_msg.
Dispatchshortmsg is used internally by MFC to distribute Windows messages. On1_msg is used to pass and send messages and update the status of user interface objects.
Cve-target's default Implementation of on1_msg: searches for the message processing function of the specified command message in the message ing array of the class and base class of the current Command target (referred to as this.
Here, we use the virtual function getmessagemap to obtain the message ing entry array _ messageentries of the Command target class, and then match the message ing entries with the same Command Message ID and control notification code in the array. Getmessagemap is a virtual function, so you can confirm the exact class of the current Command target.
If a matched message ing entry is found, use dispach1_msg to call this processing function;
If not, use _ getbasemessagemap to obtain the message ing array of the base class, and search until all the base classes (to csf-target) are found or searched;
If no value is found, fasle is returned.
Each Command target class derived from cve-target can override on1_msg and use it to determine whether a command can be processed. If not, call on1_msg of the next command target, send the command to the next command target for processing. Generally, when a derived class overwrites on1_msg, the override on1_msg of the base class is called.
In the MFC framework, the target classes of some MFC commands overwrite on1_msg. For example, the framework window class overwrites this function and implements the standard command message sending path of MFC. If necessary, the application can also override ondomainmsg and change the sending rules in one or more classes to implement different sending paths than the standard framework sending rules. For example, in the following situations, the command can be passed to a non-MFC default object in the class to interrupt the sending order; in a new non-default object or a command target that may need to output commands.
Message ing content
The code generated by classwizard shows that message ing is basically divided into two parts:
In the header file (. h) There is a macro declare_message_map () which is placed at the end of the class and is a public attribute. What corresponds to this is in the implementation part (. CPP) added a message ing table with the following content:
Begin_message_map (current class, base class of the current class)
// {Afx_msg_map (cmainframe)
Message entry
//} Afx_msg_map
End_message_map ()
However, only these two items are far from enough to complete a message. If a message works, there must be three parts to collaborate:
1. Add the corresponding function declaration to the class definition;
2. Add the corresponding message ing entry in the message ing table of the class;
3. Add the corresponding function body to the class implementation;
Message Addition
With the above as the basis, we will do the most familiar and common work: Add messages. There are two main methods to add an MFC message: automatic/manual. Let's take these two methods as an example to explain how to add a message.
1. Use Class Wizard to automatically add
Select View> Class Wizard from the menu. You can also right-click and select Class Wizard to activate Class Wizard. Select the message map tag and select the class from the class name combo box that we want to add the message. In the object IDs list box, select the class name. In this case, the messages list box shows that most (if not all) of the class can overload member functions and window messages. Class overload is displayed on the top of the list, expressed by uppercase and lowercase letters of the actual fictional member function. Others are window messages, which appear in uppercase letters and describe the message IDs that can be responded to by the actual window. Select the added message and click Add function. Class Wizard automatically adds the message.
Sometimes, the message we want to add should appear in the message list, but it cannot be found. What should we do? Don't worry. We can use the class info label on the Class Wizard to expand the message list. On this page, find the message filter combo box and use it to change the options in the messages list box on the homepage. Here, we select window to display all window messages. In one case, the messages you want to add can appear in the message list box. If not, then let's look at it later :)
2. manually add message processing functions
If you still cannot see the desired message in the messages list box, the message may be ignored by the system or created by yourself. In this case, you must manually add the message. Based on the three parts of the message work we mentioned above, we can process them one by one:
1) Add the handler function declaration in the. h file of the class, and add the Declaration immediately after the //} afx_msg line. Note: it must start with afx_msg.
Generally, the best place to add a processing function declaration is under the table maintained by Class Wizard in the source code, but outside of the {} arc that marks its field. Everything in the ARC will be destroyed by Class Wizard.
2) then, find the //} afx_msg_map row in the. cpp file of the user class, and add the message entry item immediately after it. It is also placed outside {}.
3) Finally, add the entity of the message processing function to the file.