Now that you have an understanding of the Windows application architecture, you are ready to learn how MFC implements a basic Windows application.
In addition to the encapsulation of the Win32 API, MFC defines a small group of classes to represent common application objects, and establishes relationships within this group to implement fundamental Windows application behaviors. These application architecture classes, along with a number of global functions, comprise an application framework that you can use as a basis for constructing applications. You can use the MFC AppWizard to generate a set of classes that is derived from the framework classes. You can build upon these classes to construct an application to suit your individual requirements.
After this lesson, you will be able to:Estimated lesson time: 40 minutes
- Describe how the MFC application framework implements a Windows application.
- Describe the role of the application class and the main window class in the application framework.
- Describe how the AppWizard can be used to generate a basic framework application.
- Describe how Windows messages are handled by the application framework.
The MFC application framework implements a basic Windows application architecture by providing:
The MFC class CWinApp represents the application as a whole. CWinApp is derived from CWinThread, the class that represents a thread in an MFC application. CWinApp represents the primary thread of an application's process, and encapsulates the initialization, running, and termination of a Windows-based application. These behaviors are implemented by the member functions described in Table 3.3.
Table 3.3 CWin App Member Functions
CWinApp member function | Purpose |
---|---|
InitInstance() | Initializes each new instance of your application running under Windows. Displays the application's main window. |
Run() | Provides the implementation of the message loop. |
OnIdle() | Called by the framework when no Windows messages are being processed. You can override this function to perform background tasks. |
ExitInstance() | Called each time a copy of your application terminates. |
An application built on the MFC framework must implement exactly one class derived from CWinApp. You must also provide your own overridden version of the InitInstance() member function. The InitInstance() function is called directly by the WinMain() function, and is the proper location for initialization specific to your application.
The framework provides a standard WinMain() function for your application. WinMain() calls a number of global functions to perform standard initialization services such as registering window classes. It then calls the InitInstance() member function of the application object to initialize the application. Next WinMain() calls the Run() member function of the application object to run the application message loop. The message loop will continue to acquire and dispatch messages until a WM_QUIT message is received, at which point it calls the application object's ExitInstance() function and returns. WinMain() will then call cleanup routines and terminate the application.
An essential component of a Windows application is the main window, which represents the principal interface to your application. This might be a simple dialog box; or it might be a frame window, which is a resizable window that houses the application menu, the toolbars, and the window client area. In either case, a framework application should provide a class—derived from the CDialog class or from the CFrameWnd class—that the application can use to create the main window object. The main window is initially displayed by the application object's InitInstance() function.
As an exercise, return to the MyApp project you created in Chapter 2 to view the framework classes that have been created for you.
Figure 3.4 The MyApp project ClassView
In Lesson 2, you learned that one of the key tasks of Windows application development is the mapping of system messages to routines that will handle them. In Win32 applications constructed without MFC, you supply a window procedure for each type of window class that is registered. These window procedures are often implemented as switch statements based on the value of the message ID. In an application based on the MFC framework, messages are handled by member functions of the application's classes. These can be classes that you create yourself or classes generated by the AppWizard for your application. The mapping of messages to their appropriate handler function is achieved by the use of message maps.
A message map is a table, declared within a class definition, that maps system messages to the member functions of the class. The message map contains entries that link the message IDs to the handler functions. The message map recognizes four types of messages, as described in Table 3.4.
Table 3.4 MFC Message Types
Message type | Description |
---|---|
Windows | Windows messages are generated by the operating system. They inform the application about window creation, impending window destruction, mouse and keyboard events, changes to the system colors, and anything else that can affect the operation of the application. The identifiers for these mes- sages generally begin with the WM_ prefix. Windows messages are usually handled by the window to which Windows sends the message, such as an application's main frame window, or by a dialog box. |
Command | Command messages are generated from user interaction with the user interface—for example when the user selects a menu item, clicks a tool- bar button, or presses a shortcut key. When one of these events occurs, a WM_COMMAND message with command-specific parameters is sent to the application. Command messages are routed by the framework to the application objects. This command routing is a feature that allows the application to handle the message in the class most closely associated with the message. |
User interface update command | User interface update command messages are generated from within an application by the application framework—that is, they are MFC-specific. They signal the application to update the status of user interface elements such as menu items or toolbar buttons. For example, before a menu is dis- played, the application framework will send the application an appropriate update command message that gives the application the opportunity to modify the menu command state—whether it should be available, unavailable, or displayed with a check mark. |
Control notification | Control notification messages are sent from controls and other child windows to their parent windows. These are generally sent as WM_COMMAND messages with control notification parameters. For example, an edit control sends its parent a WM_COMMAND message containing the EN_CHANGE control notification code when the user has taken an action that might have altered text in the edit control. |
MFC makes it easy to implement message maps. Any class that is derived from the CCmdTarget class is capable of supporting a message map. All the framework classes generated with the AppWizard will be created with a basic mes- sage map structure. You can use the ClassWizard tool to create new classes that implement message maps and to maintain the message maps of existing classes by adding and removing message map entries. Use ClassWizard to maintain your classes' message maps wherever possible to save time and to ensure that the message maps are correctly implemented.
In this section, you will learn how to use ClassWizard to add handlers for a Windows message and for a command message. In subsequent chapters, you will learn how to implement user interface update command messages and control notification messages. By looking at the code that is generated, you will learn how message maps are implemented in an MFC application.
In the following exercise, you will add a handler to display a message box when the user clicks in the client area of your MyApp application window. The client area is the area enclosed by the frame window, where application data is usually displayed.
AfxMessageBox("You clicked?"); |
so that your code reads as follows:
void CMyAppView::OnLButtonDown(UINT nFlags, CPoint point) { AfxMessageBox("You clicked?"); CView::OnLButtonDown(nFlags, point); } |
In the following exercise, you will add a handler for a command message that is generated when the user clicks Paste on the Edit menu in the MyApp application.
AfxMessageBox("MYAPP does not support the Paste command"); |
so that your code reads as follows:
void CMyAppApp::OnEditPaste() { AfxMessageBox("MYAPP does not support the Paste command"); } |
When you use ClassWizard to add handler functions, it automatically performs the following tasks for you:
//{{AFX_MSG(CMyAppApp) afx_msg void OnAppAbout(); afx_msg void OnEditPaste(); //}}AFX_MSG DECLARE_MESSAGE_MAP() |
Note the DECLARE_MESSAGE_MAP macro. This is an essential part of the message map implementation and is added to the class by the AppWizard or ClassWizard. You will learn how to create classes using ClassWizard in subsequent chapters.
You have already seen, and added code to, the function stub that ClassWizard provides.
BEGIN_MESSAGE_MAP(CMyAppApp, CWinApp) //{{AFX_MSG_MAP(CMyAppApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) //}}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() |
You can see that ClassWizard has added an ON_COMMAND entry macro to the message map. The structure of this macro is quite straightforward. The first parameter is the ID of the command. (You will learn more about assigning command IDs in the next chapter.) The second parameter is the name of the handler function.
The OnLButtonDown () function handles the WM_LBUTTONDOWN message and is declared in MyAppView.h as:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point); |
The corresponding message map entry in MyAppView.cpp is:
ON_WM_LBUTTONDOWN() |
Because Windows messages are handled by overloaded virtual functions on the CWnd base class, there is no need to supply the handler function name as a parameter. The MFC framework automatically maps the message to the appropriate virtual function. Windows messages often have additional information attached to them in the form of parameters. The framework will take care of extracting the information from these parameters and passing it to the handler functions. If you look at the CMyApp::OnLButtonDown function declaration, you will see that it takes the following: two parameters, derived from the WM_LBUTTONDOWN message parameters, which indicate the current screen position of the mouse pointer; and a flag value to indicate whether any of the "virtual keys" (for example, CTRL or SHIFT) are also being depressed.
MFC defines four message map entry macros, listed in Table 3.5, to correspond to the four types of messages that can be handled by the message map.
Table 3.5 Message Map Entry Macros
Message type | Macro form | Parameters |
---|---|---|
Standard Windows message | ON_WM_XXX (where XXX is the name of the message) | None |
Command message | ON_COMMAND | Command ID, Handler Name |
Update command | ON_UPDATE_COMMAND_UI | Command ID, Handler Name |
Control notification | ON_XXX (where XXX is the name of the parameter indicating the control notification) | Control ID, Handler Name |
You may have noticed that all ClassWizard-generated code is placed within comment blocks beginning with {{AFX_MSG and ending with }}AFX_MSG. These tags denote areas that are modified by ClassWizard. You are allowed to add message map entries or handler function declarations manually, but you should add them outside of these sections. Failure to do so might result in improper functioning of ClassWizard. The AFX_MSG modifier that precedes the handler declaration is also used by ClassWizard. As far as the compiler is concerned, it has no value.
If you use ClassWizard to delete message map entries, it will also delete the function declaration in the class header file. It will not delete the function implementation, which might contain code that you have written.
You will recall that command messages are routed by the framework to the application objects. This command routing feature allows the application to handle the message in the class most closely associated with the message. The MFC application framework routes commands through a standard sequence of command-target objects (defined by the classes in your application that are derived from CCmdTarget and implement message maps) to see if any of them provide a handler for the command. Each command-target object checks its message map to see if it can handle the incoming message.
This command routing sequence can sometimes be used to your advantage. For example, you can set up an application shortcut key to send a command message with ID_SAVE_WINDOW_STATE. This shortcut key indicates that the user wants to save the current window's settings. Some window objects in the system might provide their own implementations of the associated handler function OnSaveWindowState(). Other windows, such as temporary dialog boxes and help screens, might not. The command routing sequence will check the message map of the currently active window to see if it implements the function OnSaveWindowState(). If it does not, the message map of the parent window will be checked. If that does not provide a handler, the message map of the application object can be checked.
The command routing sequence varies according to context. To find out more about command routing, search for "command routing" in the Visual C++ Help file.
The MFC application framework is a small group of interrelated classes that, together with a number of global functions, implement a basic Windows application. Using the AppWizard, you can easily generate a set of classes, derived from the framework classes, on which they can build an application to suit their own particular requirements.
The framework classes include the CWinApp class, which encapsulates the application as a whole. It exposes several functions that are called by the frame-work's implementation of the WinMain() function to handle the initialization, running, and termination of a Windows-based application. The CFrameWnd class encapsulates a frame window, which is a main application window that is capable of hosting application menus, toolbars, and a window client area.
In an application based on the MFC framework, messages are handled by member functions of the application's classes. The mapping of messages to their appropriate handler function is achieved by the use of message maps, a feature supported by classes derived from the MFC class CCmdTarget. Message maps are tables that are declared within a class definition to link message IDs to member functions of the class. You should use the ClassWizard tool to add and remove message map entries.
In this lesson, you learned how to use ClassWizard to add handlers for Windows messages. Windows messages are generated by the operating system to inform the application of changes in the Windows environment. These changes include mouse and keyboard events and command messages. Command messages are generated by user interaction with user interface elements such as menus and toolbars.
Command messages are routed by the framework to the application's command target objects in a specific sequence until an object is found that provides the appropriate handler. This command routing is a feature that allows the application to handle the message in the class most closely associated with the message.