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. We will analyze the message processing in MFC tomorrow.
Unfinished (To be continued ...)