How Do I Respond to Windows Messages?
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");
}