Working with Windows messages
by Kent Reisdorph
In the early days of Windows programming, the programmer had a lot of work to do to create even the simplest of programs. Much of the work entailed handling dozens of Windows messages. This was accomplished by implementing huge switch statements in the application’s window procedure. Because of this, it was vital for the early Windows programmer to have intimate familiarity with Windows messages. This knowledge included understanding of vast numbers of Windows messages and also a good working knowledge of how the Windows message system worked.
With the advent of Windows framework libraries (such as the VCL), the programmer was relieved from having to know the minute details of the Windows message system. As a VCL programmer you, at least for the most part, don’t ever have to worry about your application’s window procedure—everything you need to know is passed on to you as VCL events.
This ease of use can be problematic in that new programmers never learn the basics of the Windows messaging system. There are times when you need to handle messages not surfaced as VCL events. There are other times when you need to send a Windows message in order to accomplish some specific task not covered by the VCL methods.
The Windows message system
Before I get into the specifics of working with Windows messages, I need to explain the basics of the Windows message system. The following sections explain windows messages, how Windows generates those messages, and how an application processes messages.
Windows is event driven
The Windows operating system is driven by messages. When an application is hidden by another window and then brought to the front, for example, Windows sends that application a WM_PAINT message. The WM_PAINT message instructs the application to redraw its main window. Likewise, Windows sends the application a WM_MOUSEMOVE message every time the mouse moves over the application. From this you can deduce that there may be thousands of Windows messages flying around at any given moment in time. By the way, I’m using the term “application” loosely here; Windows doesn’t actually send the application a message, but rather sends the application’s main window a message.
There are three basic types of Windows messages. The first type is used to instruct a window to perform a specific action. The WM_PAINT message mentioned earlier falls into this category.
The second message type is used to notify a window that some event has occurred. As an example, consider an edit control on a main form. When the user types in the edit control, Windows sends an EN_CHANGE message to the edit control’s parent window to notify the parent that the contents of the edit control have changed. Similarly, when a button is clicked, Windows sends the parent window a BN_CLICKED notification message. Many of the events defined for VCL components are generated in response to notification events.
The third type of message is the user-defined message. This is a message that the programmer defines for use within the application.
Message queues
To manage all of these messages, Windows maintains message queues. A message queue is a FIFO (first in, first out) list of messages. Messages are placed in the queue and processed in order they were received. Windows will process the messages in the queue when it has time. Windows maintains a global system message queue and separate message queues for each GUI thread. For simplicity sake, you can consider a thread queue as specific to a particular application.
Not all messages go through the message queue. Some messages are sent directly to the application’s window procedure (explained in the next section). Messages can be removed from the message queue using the GetMessage() function. Messages in the queue can be examined using the PeekMessage() function. PeekMessage() allows you to view a message but not remove it, or to remove the message from the queue and process that message manually. Processing the message manually is accomplished by calling TranslateMessage() and then DispatchMessage(). I won’t explain usage of these functions in this article as they are rarely needed in VCL programming.
Window procedures
When a window class is created by an application, its window procedure is registered with the operating system. Each time a message is generated, Windows will call the window procedure associated with that window.
A window procedure is a callback function. The declaration for a window procedure might look like this:
LRESULT CALLBACK WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
The hwnd parameter identifies the window for which the message is intended. Several windows can use a single window procedure and the hwnd parameter is how the application determines the window to which the message is being sent. The uMsg parameter contains the ID of the message being sent. The Windows headers define a macro for each message. The WM_PAINT message, for example, has a value of 0x000F hexadecimal. The wParam and lParam parameters contain information specific to the type of message being sent. A particular message may utilize neither of these parameters, just the wParam, or both wParam and the lParam. For example, the WM_PAINT message passes the device context handle of the window to be painted in the wParam, and nothing (zero) in the lParam.
You have no doubt seen an application that, for one reason or another, isn’t repainting its main window. This can be due to many reasons, but the bottom line is that Windows is trying to send messages to the window procedure but the window is unable to process those messages.
A straight C/C++ Windows application has to register and maintain a window procedure. Fortunately, the VCL takes care of this for you.
VCL message handling
The VCL automatically registers a window procedure, handles messages, and passes those messages on to you in the form of VCL events. The VCL obviously includes a large number of events. Most of these events are generated in response to a Windows message (some are generated via other means but those events aren’t relevant to this article). The OnKeyDown event, for example, is fired when Windows sends the application a WM_KEYDOWN message.
Not all windows messages are surfaced as VCL events. There are times when you need to handle a message for which there is no corresponding VCL event. For those messages you will have to make use of a message map. I have covered message maps in past articles, so I won’t explain them here. See the C++Builder help on MESSAGE_MAP for more information on message maps.
VCL classes representing a window all have a WndProc() method. You can override this method to intercept or respond to messages before the VCL gets a chance to handle them. This is not typically necessary when writing applications. Component writers, however, may need to override WndProc() in order to perform specialized message handling.
Sending messages
In addition to specialize message handling, you may need to send Windows messages from your application. A good example is the WM_SETREDRAW message. This message is used to temporarily disable painting of a particular control and later to re-enable painting. For example, let’s say you needed to add hundreds or thousands of nodes to a TTreeView. Adding a large number of nodes to a tree view can take a long time because the tree view repaints itself as each node is added. By disabling painting of the tree view, items are added much more quickly.
There are three ways to send messages to a VCL application. They are discussed in the next three sections.
The SendMessage() function
The API’s SendMessage() function is used to send a message directly to a window’s window procedure. SendMessage() is a synchronous call; the call to SendMessage() will not return until the window procedure has processed the message. SendMessage() bypasses the application’s message queue. Take the WM_SETREDRAW message mentioned earlier, for example. The code to disable painting of a TTreeView, add items, and re-enable painting might look like this:
SendMessage( TreeView->Handle, WM_SETREDRAW, 0, 0); // Add items to the tree view SendMessage( TreeView->Handle, WM_SETREDRAW, 1, 0);
Note that I pass the handle of the tree view control in the first parameter. This is the window (a control in this case) for which the message is destined. In the case of WM_SETREDRAW, you pass 0 for the wParam to disable painting, and 1 to enable painting again. The lParam is not used so I simply pass 0 for this parameter.
Some messages return a value. The purpose of the return value varies widely depending on the message being sent. If you are sending a message and want to examine the return value from that message, you must use SendMessage().
The PostMessage() function
The PostMessage() function is used to place a message in a window’s message queue. Unlike SendMessage(), PostMessage() is an asynchronous call. That is, PostMessage() returns immediately after posting the message to the message queue. The application continues on its way and the message will eventually make its way to the application’s window procedure.
PostMessage() can be used in situations where you don’t need a message’s return value. It is also used in situations where you wish to pass a message to windows but need to interject a slight delay before that message is handled.
The VCL’s Perform() method
VCL classes that correspond to a window (TForm and all windowed components) have a method called Perform(). This method sends a message directly to the control’s window procedure, bypassing the Windows message system altogether. Perform() works much like the API function SendMessage(). The earlier code presented for the WM_SETREDRAW message could be rewritten to use Perform() as follows:
TreeView->Perform(WM_SETREDRAW, 0, 0); // Add items to the tree view TreeView->Perform(WM_SETREDRAW, 1, 0);
Ultimately, calling Peform() is the fastest way of sending a message to a VCL windowed component. In most cases it doesn’t matter whether you use Perform() or SendMessage(); the results are practically the same.
TApplication’s ProcessMessages() method
No explanation of Windows messages would be complete without at least brief coverage of the VCL Application object’s ProcessMessages() method. This method queries the application’s message queue for waiting messages and processes them.
Any time you have a lengthy process in your application (such as a long for loop), you should call Application->ProcessMessages() during that process. If you do not do this, your application will appear to be locked up. That is, its main form will not repaint, it cannot be moved, maximized, minimized, and so on. Here’s an example of ProcessMessages():
for (int i=0;i<count;i++) {
// processing code here
Application->ProcessMessages();
}
Naturally, calling ProcessMessages() takes time. For long loops you may want to call ProcessMessages() only periodically rather than on each iteration. For example:
for (int i=0;i<count;i++) {
// processing code here
if ((i % 1000) == 0)
Application->ProcessMessages();
}
In this case ProcessMessages() will only be called once for every 1000 iterations of the loop.
Conclusion
The VCL takes much of the pain of message handling out of the programmer’s hands. Still, there are times when you need to handle Windows messages yourself or send messages to a particular window. In those cases, it is important that you understand how the Windows message system works.