How Do I Respond to Windows Messages?

Back Up Next

Just about everything that happens in a Windows program happens because of messages. There are all kinds of messages, but the most common are Windows messages, whose constants are usually prefaced with WM; command messages, whose constants are usually prefaced with CM; and control messages, whose constants usually begin with ID or I DC.

Windows messages are sent by the Windows system when something happens. Examples are WM_LBUTTONDOWN, which is sent when the user presses the left mouse button, and WM_TIMER, which is sent when a Windows timer goes off. Command messages are usually associated with the application's menu items. For example, in an OWL program, when the user selects the File menu's Exit command, the application receives a CM_EXIT message. Finally, control messages are associated with things such as buttons. When a user clicks a dialog box's OK button, for example, the dialog box receives an OWL IDOK message.

The Windows messages are all predefined; however, command messages and control messages depend on your application, and may be defined either by you or by OWL. So that your windows can respond to the messages sent to them, your window classes must contain response tables, which associate messages with the functions that are to handle them.

The Solution

To declare a response table in a class, you use the DECLARE_RESPONSE_TABLE macro, which requires as its single argument the name of the class for which the table is being declared:

DECLARE_RESPONSE_TABLE(TWndw);

This declaration is usually placed right before the ending brace of the class declaration. The table itself is defined outside the class, usually right after the class's declaration:

DEFINE_RESPONSE_TABLE1(TWndw, TFrameWindow)
EV_WM_LBUTTONDOWN,
EV_COMMAND(CM_MYCOMMAND, CmMyCommand), END_RESPONSE_TABLE;

You start the table with the DEFINE_RESPONSE_TABLE macro. The name of the macro must be followed by the number of immediate base classes from which your class is derived. As is the case with TWndw, this value almost always is 1. The exception is classes derived through multiple inheritance. After the macro name and immediate base class count, you provide the name of the class for which the table is being defined, as well as the names of all immediate base classes. In the example, the only immediate base class for TWndw is TFrameWindow, so it is the only additional class listed in the macro. The class names are enclosed in parentheses and separated by commas.

Borland has predefined response-table macros for all standard Windows messages. To enable your window to respond to a particular Windows message, you need only include its macro in your response table and provide the matching response member function. You can determine a macro's name by adding EV_ to the front of the Windows message name. So, the response table macro for the WM_LBUTTONDOWN message is EV_WM_LBUTTONDOWN, the macro for WM_PAINT is EV_WM_PAINT, the macro for WM_MOUSEMOVE is EV_WH_MOUSEMOVE, and so on.

To create a response-table entry for a command or control message, use the EV_COMMAND macro. The macro's two arguments, enclosed in parentheses, are the message ID and the name of the function that will respond to that message. Note that you do not include the function's parameters, or even its parentheses, in the table. Also, every response-table entry must be followed by a comma.

The predefined macros for Windows messages in the response table are designed to automatically match specific message-response functions to the appropriate Windows messages. EvLButtonDown() is, for example, the response function that matches the WM_LBUTTONDOWN message. You must follow a strict set of naming rules for these functions so that Borland C++ can match the function with the message it is meant to handle.

You determine the necessary function name for a particular Windows message by taking the message's name, replacing the WM_ with Ev, and then spelling the rest of the function name as it appears in the message name, except using uppercase letters for the first letter of each "word" and lowercase letters for the rest. So, WM_LBUTTONDOWN becomes EvLButtonDown, WM_PAINT becomes EvPaint, WM_MOUSEMOVE becomes EvMouseMove, and so on. In the preceding class declaration, you can now see that the member function EvLButtonDown() is meant to respond to WM_LBUTTONDOWN messages.

After you have written response-table entries for each message your window class will handle, you must end the table. You do this with the END_RESPONSE_TABLE macro, followed by a semicolon.

Now that you have your response table defined, define the response functions for the Windows messages:

void TWndw::EvLButtonDown(uint, TPoint&)
{
    MessageBox("Got the click!", "Message");
}

How do you know what types of arguments are returned and received by a particular message-response function? The prototypes for each of the many message-response functions for standard Windows messages are listed in your ObjectWindows reference guide that came with your copy of Borland C++ 4.x. As you'll see when you look at this list of prototypes, one of the advantages of using Borland's predefined response-table macros is that the associated Windows message is "cracked" before being passed to your response function. In other words, Borland C++ automatically extracts the relevant values passed in the WPARAM and LPARAM parameters.

For example, in the case of the WM_LBUTTONDOWN message, Windows encodes a virtual key flag in WPARAM and the x and x coordinates of the mouse pointer in LPARAM. But, instead of forcing you to extract this information on your own, Borland C++ automatically extracts the values and sends them to your EvLButtonDown () function, where the virtual key flag is received as uint and the mouse's x and y coordinates are in a TPoint object.

When you write a response function for a command message or a control See "How Do I message, the function usually receives no parameters. Moreover, you can Send My Own name the function anything you like, although the convention is to use the Windows same name as the message constant, except spelling the name in both upper-Messages?," and lowercase letters:

void TWndw::CmTest()
{
  MessageBox("Got the menu message!", "Message");
}


Copyright © 1998-2001 Yura Bidus. All rights reserved.