Abstract: Windows Programming and DOS programming, a big difference is that Windows programming is event-driven, message transmission. Therefore, to learn windows programming well, you must
I have a clear understanding of the message mechanism. This article hopes to make a comprehensive analysis of message transmission.
1. 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 is unique
A notification is sent to Windows to tell the application that something has happened. For example, click the mouse, change the window size, and press a key on the keyboard.
Windows sends 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
The record contains the coordinates when you click the mouse. The record type is MSG. MSG contains the message information from the Windows application message queue.
The statement in Windows is as follows:
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; // additional information of a 32-bit message. The exact meaning depends on the message value.
Lparam; // additional information of a 32-bit message. The exact meaning depends on the message value.
DWORD time; // the time when the message was created.
Point pt; // The cursor/cursor position in the screen coordinate system when the 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. System
Messages are generated to respond to changes brought about by applications. For example, the application changes the system font and the form size. Applications can generate messages for the form to execute tasks,
Or communicate with windows in other applications.
2. 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 screen objects of any type, because Win32 can maintain the handles (Windows, dialog boxes, buttons, and edits) of most visual objects.
Box ).
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 is a constant.
The naming method indicates the meaning of the message. 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 tell window
The customer area of the form is changed and needs to be re-painted. 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.
3. 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
Message.
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 from 0xc000
Messages in the 0xffff range 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
Iv. Classification of messages
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,
Both 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 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.
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 only applies to standard window controls such
Button, list box, combo box, edit box, and Windows public controls such as tree view and list view. For example, click or double-click a control and select
Text-splitting 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 displayed from the control window.
Port 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 messages sent to the parent window can also be seen as a control notification message. The messages generated by clicking the mouse can be directly processed by the main window. However
And then handed over to the control window for processing.
The window messages and control notification messages are mainly processed by the window class (that is, directly or indirectly by the cwnd class derived class. Command consumption is relative to window messages and control notification messages.
The range of objects to be processed is wide. It can be processed not only by the window class, but also by the document class, document template class, and application class.
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 greater use.
Home 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
5. 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, all threads generate
There is no message queue. Only when the thread calls the GDI function for the first time, the system creates a message queue for the thread. The queue message is sent to the system message queue and then to the thread for elimination.
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
System message queue, which is processed by the Windows system. In Windows, a message is retrieved from the system message queue at an appropriate time.
The MSG message structure determines the message to be sent to that window, and then sends the retrieved message to the corresponding queue of the thread that created the window.
I am worried, and Windows is busy with my own affairs. When a thread sees a message in its own message queue, it extracts it from the queue and sends it to the appropriate
Process the window.
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, the same
Multiple wm_paint messages in windows are merged into a wm_paint message, and all invalid regions are merged into an invalid region. The purpose of merging wm_pain is to reduce the number of clicks
The number of new windows.
Non-queue messages will bypass the system message queue and thread message queue and directly send messages to the window. The system sends a non-queue Message notification window. For example
The system sends wm_activate, wm_setfocus, and wm_setcursor in a window. These Message notification windows are activated. Non-queue messages can also be
The application calls the system function to generate. For example, when the program calls setwindowpos, the system sends the wm_windowposchanged message. Some functions also send non-queue messages
For example, the function we want to talk about below.
6. 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.
Message sending functions include sendmessage, sendmessagecallback, sendpolicymessage, and sendmessagetimeout.
Postmessage, postthreadmessage, and postquitmessage; I only know broadcastsystemmessage,
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 until the message is processed. However, it should be noted that if the window for receiving messages
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
The Window System switches to the corresponding thread and calls the corresponding window function. This message is not put into the target application queue. Function return value
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 to the message of the thread that created the hwnd window.
In the queue, this function controls the return immediately after the message is processed. Note that if the hwnd parameter is hwnd_broadcast, the message will be sent to the system.
All overlapping windows and pop-up windows in the system, but the subwindow does not receive the message. If the hwnd parameter is null, this function is similar to setting the dwthreadid Parameter
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 will be processed immediately.
Whether to return immediately. The sent message is processed immediately, and the function returns the message after processing. The sent message is not processed immediately.
Will be processed until the application is idle, but the function will return immediately after the message is placed.
In fact, there is no big difference between the process of sending messages to a window and the process of directly calling a window. The only difference between them is that you can request
The operating system intercepts all sent messages, but cannot intercept direct calls to the window processing process.
Messages sent in sending mode usually correspond to user input events, because these events are not very pressing and can be buffered slowly, such as the mouse and key
Disk messages are sent, while buttons and other messages are 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 a specified receiver, which can be an application, a installable driver, a network driver, or a system-level device drive.
Dynamic 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 ignores the returned value.
VII. 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:
If hwnd is null, getmessage gets messages from any window that calls the function application. If both wmsgfiltermin and wmsgfiltermax are
If the value is 0, getmessage returns all available messages. After the function is obtained, messages other than the wm_paint message in the message queue will be deleted.
Wm_paint is deleted only after it is 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, it is placed in the structure referred to by lpmsg. However, unlike getmessage,
The peekmessage function does not return a message until it is put into the queue. Similarly, if hwnd is null, peekmessage obtains any of the applications that call this function.
For a window message, if hwnd =-1, the function returns only the message sent by the postappmessage function whose hwnd parameter is null. If wmsgfiltermin and
If wmsgfiltermax is 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.
The message is 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 until a new message is put into the application
In a sequential queue.
8. 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
It will not be returned until a message arrives. If you pass a window handle as the second parameter to getmessage, only messages in the specified window can be transmitted from the queue
. Getmessage can also filter messages from the message queue and only accept messages that fall within the range of the message queue. Getmessage/
Peekmessage specifies a message filter. This filter is a range of message identifiers, a form handle, or both. When the application wants
It is useful to find a message in the message queue. The wm_keyfirst and wm_keylast constants are used to receive all keyboard messages. Wm_mousefirst and
The wm_mouselast constant is used to receive all mouse messages.
Then, translateaccelerator judges whether the message is a key message and an acceleration key message. If yes, the function converts several key messages
A callback function that passes a key message to the window. After the acceleration key is processed, the function translatemessage will convert the two buttons wm_keydown and wm_keyup
Replace it with a wm_char, but 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. Wm_destroy 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 indicates 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.
IX. Window Process
The Window Process is a function used to process all messages sent to this window. Any window class has a window process. Use the same window for the same class
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 distinguish
Same message, 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 do this.
. 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.
Perform the 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.
Streaming, we can divide the switch statement into smaller functions, each Message corresponds to a small function, the advantage of doing so 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.
10. 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 using one of the three major features of C ++: Virtual Machine implementation.
The current message transmission, but after analysis, we can see that things are not what 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. In the cwnd class
There are about 110 messages, and there are other MFC classes. There are too many messages. In C ++, every derived class used in the program must have a vtable, and every virtual
The function occupies a 4-byte entry address in the vtable. In this way, the application requires a large memory kb for each specific type of window or control.
Small tables to support virtual message control functions.
If the window or control above can be barely implemented, what about menu command messages and button command messages? Because different applications have different menus and
Button. What should we do? This message ing System in the MFC Library avoids the use of large vtables, and can process various types of messages while processing common Windows messages.
Command messages of various applications.
To put it bluntly, the message mechanism in MFC is essentially a huge one-to-one correspondence table of messages and their processing functions, and then some internal application frameworks that analyze and process this table
Program code. 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 is
This class has designed many member functions and some member data 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.
The default Implementation of cve-target on 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 same Command Message ID in the array.
Message ing entries with the same notification code. 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 not found, return fasle.
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, the next command can be called.
Send the on1_msg 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, change the sending rules in one or more classes, and implement different sending paths from the standard framework sending rules. For example
The command can be passed to a non-MFC default object in the class where the sending order is to be interrupted; in a new non-default object or a command that may need to be passed out
Target.
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)
File: // {afx_msg_map (cmainframe)
Message entry
File: //} afx_msg_map
End_message_map ()
However, only these two items are far from enough to complete a message. To make a message work, 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 message
Map label: select the class from the class name combo box that we want to add messages. In the object IDs list box, select the class name. In this case, the messages list box
Display most (if not all) of the class to reload member functions and window messages. Class overload is displayed on the top of the list, with 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 message we want to add, click Add function and press
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 Class Wizard
Class info label 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. This
Select window to display all window messages. In one case, the messages you want to add can appear in the message list box.
, Then let's look at it later :)
2. manually add message processing functions
If the message we want is still not visible in the messages list box, the message may be ignored by the system or created by yourself. In this case
Must be manually added. 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. These
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.
Deeply parse the message transmission mechanism in VC