C++ Course Listing

 

Windows C++ Messages

by Matthew Martin

Contents

Message Types

The WinMain() Routine

The WinProc() Procedure

Messaage Mapping

The Class Wizard

top of page

Messages are an important aspect of C++ programming for Windows. Messages are passed between objects (including the main window of the application), between applications and between applications and the Operating System (OS).

One of the most common messages is the WM_MOUSEMOVE message, which is usually passed to the operating system to deal with. The OS redraws the mouse cursor at the new location. Many of this type of low-level type of message may be largely be ignored by the programmer because they are dealt with by the OS. This means less programming work and allows the programmer to concentrate on the higher-level functionality of the program. This is all made possible through the object-oriented approach adopted with MFC.

top of page


Message Types


Messages each have a name, although the OS uses an integer value (usually expressed in hexadecimal, i.e. base-16) to reference each message. The names of messages are connected to the numbers by #define statements, some examples are shown below. The use of the names means that the programmer can refer to messages in a manner more easily remembered than the hexadecimal numbers. There are a very large number of messages available and it is possible to add your own as well. Messages can have two parameters and they also are aware of which window they are for.

#define WM_SETTEXT 0x000C
#define WM_GETTEXT 0x000D
#define WM_GETTEXTLENGTH 0x000E
#define WM_PAINT 0x000F
#define WM_CLOSE 0x0010

There are almost 900 messages available in the MFC. In order to make messages more readily identifiable they are divided into groups depending upon what type of class can receive the messages. This is possible because different classes are only able to receive certain messages. The type of message is determined by what type of class can receive (catch) the message. This is indicated in the message name by the letters preceding the underscore in the message name.

The table on the below shows windows message prefixes and the Window types they refer to, i.e. can be caught by.

Prefix Window Type
ABM, ABN Appbar
ACM, ACN Animation control
BM, BN Button
CB, CBN Combo box
CDM, CDN Common dialog box
CPL Control Panel application
DBT Any application (device change message)
DL Drag list box
DM Dialog box
EM, EN Edit box
FM, FMEVENT File Manager
HDM, HDN Header control
HKM HotKey control
IMC, IMN IME window
LB, LBN List box
LVM, LVN List view
NM Any parent window (notification message)
PBM Progress bar
PBT Any application (battery power broadcast)
PSM, PSN Property sheet
SB Status bar
SBM Scrollbar
STM, STN Static control
TB, TBN Toolbar
TBM Track bar
TCM, TCN Tab control
TTM, TTN ToolTip
TVM, TVN Tree view
UDM Up Down control
WM Generic window

top of page

The WinMain() Routine

When a program is run (executed) the OS must start the program at some point of the code. This entry point into the program is for a typical C or C++ program the Main() function (routine). For a Windows program the entry point into the program code is WinMain().

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


MSG msg;
if (! InitApplication (hInstance))
return (FALSE);
if (! InitInstance (hInstance, nCmdShow))
return (FALSE);
while (GetMessage (&msg, NULL, 0, 0)){
TranslateMessage (&msg);
DispatchMessage (&msg);


}
return (msg.wParam);

The WinMain() routine shown above has a while loop that calls GetMessage(). The condition for ending the loop is if the variable msg has a value of WM_QUIT, which will cause the while loop to break and the statement return (msg.wParam). Otherwise the while loop continues, testing the value of msg.

The API function TranslateMessage() makes dealing with keyboard messages easy. It stores keyboard input from the messages WM_KEYDOWN and WM_KEYUP, sending on a WM_CHAR message.
The API function DispatchMessage() calls the WndProc for the window that the message is headed for.

top of page


WinProc() Procedure


The WinProc() procedure tests messages values in a switch and case statement, as shown below.

LONG APIENTRY MainWndProc (HWND hWnd, // window handle
UINT message, // type of message
UINT wParam, // additional information
LONG lParam) // additional information
{


switch (message) {

 


case WM_MOUSEMOVE:
//handle mouse movement
break;
case WM_LBUTTONDOWN:
//handle left click
break;
case WM_RBUTTONDOWN:
//handle right click
break;
case WM_PAINT:
//repaint the window
break;
case WM_DESTROY: // message: window being destroyed
PostQuitMessage (0);
break;
default:
return (DefWindowProc (hWnd, message, wParam, lParam));

}


return (0);
}

The WinProc() procedure can become very large. In order to prevent the WinProc() reaching an unmanageable size Message Mapping is used.

top of page


Message Mapping


Messages maps consist of a .h (header or include) file and a .cpp (source) file. These are typically generated by wizards used within the Visual C++ programming environment. Message mapping allows the programmer to write the function that will handle the message, and add a message map to the new class. This means that the programmer does not have to write a series of cases in the WinProc(). Instead the MFC takes care of getting the message to your code.

An example .h file section:

//{{AFX_MSG(CShowStringApp)
afx_msg void OnAppAbout();
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of auto-generated code
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

The above code in the header (.h) file basically states that the function OnAppAbout takes no parameters. Wizards will automatically insert the code required where the comment line are.

An example message map portion of a .cpp file:

BEGIN_MESSAGE_MAP(CShowStringApp, CWinApp)
//{{AFX_MSG_MAP(CShowStringApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

The above code states that when the message ID_APP_ABOUT arrives the function OnAppAbout() is called.

We will not go into the details of how this all works behind the scenes. This information is available in various books and web sites, but is beyond the scope of our work here. The MFC and the Visual C++ IDE take care of a lot of the routing of the messages to the functions. Oe of the ain wizards used when coding messages is the Class Wizard.

top of page


The Class Wizard


The class wizard is invoked by selecting View, ClassWizard or by pressing Ctrl+W. The class wizard shows the current project name, class name, object IDs and messages that can be caught by the class (i.e. messages that the class might respond to). Buttons on the right of the class wizard provide the ability to add new classes, add a function to the class to catch the highlighted message, remove a function that was catching a message, or open the source code for the function that catches the highlighted message.

The add function button does the following:
• Adds a skeleton function to the bottom of the source file for the application
• Adds an entry to the message map in the source file
• Adds an entry to the message map in the include file
• Updates the list of messages and member functions in the dialog box
The class that catches a message is determined by the programmer, the choice is usually limited to one of the following:
• The active view
• The document associated with the active view
• The frame window that holds the active view
• The application object

by Matthew Martin

top of page