📄 ch03.htm
字号:
processing close to the functions that handle the messages, freeing you from maintaining
a giant switch statement that is all in one place. Read on to see how it's done.</P>
<P>
<H2><A NAME="Heading3"></A>Reading Message Maps</H2>
<P>Message maps are part of the MFC approach to Windows programming. Instead of writing
a WinMain() function that sends messages to your WindProc and then writing a WindProc
that checks which kind of message this is and then calls another of your functions,
you just write the function that will handle the message, and you add a message map
to your class that says, in effect, "I will handle this sort of message."
The framework handles whatever routing is required to send that message to you.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> If you've worked in Microsoft Visual Basic, you should be familiar
with event procedures, which handle specific events such as a mouse click. The message-handling
functions you will write in C++ are equivalent to event procedures. The message map
is the way that events are connected to their handlers.
<HR>
</BLOCKQUOTE>
<P>Message maps come in two parts: one in the .h file for a class and one in the
corresponding .cpp. Typically, they are generated by wizards, although in some circumstances
you will add entries yourself. Listing 3.4 shows the message map from the header
file of one of the classes in a simple application called ShowString, presented in
Chapter 8, "Building a Complete Application: ShowString."</P>
<P>
<H4>Listing 3.4  Message Map from showstring.h</H4>
<PRE>//{{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 generated code !
//}}AFX_MSG
</PRE>
<PRE> DECLARE_MESSAGE_MAP()
</PRE>
<P>This declares a function called OnAppAbout(). The specially formatted comments
around the declarations help ClassWizard keep track of which messages are caught
by each class. DECLARE_MESSAGE_MAP() is a macro, expanded by the C++ compiler's preprocessor,
that declares some variables and functions to set up some of this magic message catching.</P>
<P>The message map in the source file, as shown in Listing 3.5, is quite similar.</P>
<P>
<H4>Listing 3.5  Message Map from Chapter 8's showstring.cpp</H4>
<PRE>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)
</PRE>
<PRE>END_MESSAGE_MAP()
</PRE>
<H3><A NAME="Heading4"></A>Message Map Macros</H3>
<P>BEGIN_MESSAGE_MAP and END_MESSAGE_MAP are macros that, like DECLARE_MESSAGE_MAP
in the include file, declare some member variables and functions that the framework
can use to navigate the maps of all the objects in the system. A number of macros
are used in message maps, including these:</P>
<P>
<UL>
<LI>DECLARE_MESSAGE_MAP--Used in the include file to declare that there will be a
message map in the source file.
<P>
<LI>BEGIN MESSAGE MAP--Marks the beginning of a message map in the source file.
<P>
<LI>END MESSAGE MAP--Marks the end of a message map in the source file.
<P>
<LI>ON_COMMAND--Used to delegate the handling of a specific command to a member function
of the class.
<P>
<LI>ON_COMMAND_RANGE--Used to delegate the handling of a group of commands, expressed
as a range of command IDs, to a single member function of the class.
<P>
<LI>ON_CONTROL--Used to delegate the handling of a specific custom control-notification
message to a member function of the class.
<P>
<LI>ON_CONTROL_RANGE--Used to delegate the handling of a group of custom control-notification
messages, expressed as a range of control IDs, to a single member function of the
class.
<P>
<LI>ON_MESSAGE--Used to delegate the handling of a user-defined message to a member
function of the class.
<P>
<LI>ON_REGISTERED_MESSAGE--Used to delegate the handling of a registered user-defined
message to a member function of the class.
<P>
<LI>ON_UPDATE_COMMAND_UI--Used to delegate the updating for a specific command to
a member function of the class.
<P>
<LI>ON_COMMAND_UPDATE_UI_RANGE--Used to delegate the updating for a group of commands,
expressed as a range of command IDs, to a single member function of the class.
<P>
<LI>ON_NOTIFY--Used to delegate the handling of a specific control-notification message
with extra data to a member function of the class.
<P>
<LI>ON_NOTIFY_RANGE--Used to delegate the handling of a group of control-notification
messages with extra data, expressed as a range of child identifiers, to a single
member function of the class. The controls that send these notifications are child
windows of the window that catches them.
<P>
<LI>ON_NOTIFY_EX--Used to delegate the handling of a specific control-notification
message with extra data to a member function of the class that returns TRUE or FALSE
to indicate whether the notification should be passed on to another object for further
reaction.
<P>
<LI>ON_NOTIFY_EX_RANGE--Used to delegate the handling of a group of control-notification
messages with extra data, expressed as a range of child identifiers, to a single
member function of the class that returns TRUE or FALSE to indicate whether the notification
should be passed on to another object for further reaction. The controls that send
these notifications are child windows of the window that catches them.
</UL>
<P>In addition to these, there are about 100 macros, one for each of the more common
messages, that direct a single specific message to a member function. For example,
ON_CREATE delegates the WM_CREATE message to a function called OnCreate(). You cannot
change the function names in these macros. Typically, these macros are added to your
message map by ClassWizard, as demonstrated in Chapter 8.</P>
<P>
<H3><A NAME="Heading5"></A>How Message Maps Work</H3>
<P>The message maps presented in Listings 3.3 and 3.4 are for the CShowStringApp
class of the ShowString application. This class handles application-level tasks such
as opening a new file or displaying the About box. The entry added to the header
file's message map can be read as "there is a function called OnAppAbout() that
takes no parameters." The entry in the source file's map means "when an
ID_APP_ABOUT command message arrives, call OnAppAbout()." It shouldn't be a
big surprise that the OnAppAbout() member function displays the About box for the
application.</P>
<P>If you don't mind thinking of all this as magic, it might be enough to know that
adding the message map entry causes your code to run when the message is sent. Perhaps
you're wondering just how message maps really work. Here's how. Every application
has an object that inherits from CWinApp, and a member function called Run(). That
function calls CWinThread::Run(), which is far longer than the simple WinMain() presented
earlier but has the same message loop at its heart: call GetMessage(), call TranslateMessage(),
call DispatchMessage(). Almost every window object uses the same old-style Windows
class and the same WindProc, called AfxWndProc(). The WindProc, as you've already
seen, knows the handle, hWnd, of the window the message is for. MFC keeps something
called a <I>handle map</I>, a table of window handles and pointers to objects, and
the framework uses this to send a pointer to the C++ object, a CWnd*. Next, it calls
WindowProc(), a virtual function of that object. Buttons or views might have different
WindowProc() implementations, but through the magic of polymorphism, the right function
is called.</P>
<BLOCKQUOTE>
<P>
<HR>
<B>Polymorphism</B><BR>
</P>
<P>Virtual functions and polymorphism are important C++ concepts for anyone working
with MFC. They arise only when you are using pointers to objects and when the class
of objects to which the pointers are pointing is derived from another class. Consider
as an example a class called CDerived that is derived from a base class called CBase,
with a member function called Function() that is declared in the base class and overridden
in the derived class. There are now two functions: One has the full name CBase::Function(),
and the other is CDerived::Function().<BR>
</P>
<P>If your code has a pointer to a base object and sets that pointer equal to the
address of the derived object, it can then call the function, like this:
<PRE> CDerived derivedobject;
CBase* basepointer;
basepointer = &derivedobject;
basepointer->Function();</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<BLOCKQUOTE>
<P>In this case, CBase::Function() will be called. However, there are times when
that is not what you want--when you have to use a CBase pointer, but you really want
CDerived::Function() to be called. To indicate this, in CBase, Function() is declared
to be virtual. Think of it as an instruction to the compiler to override this function,
if there is any way to do it.<BR>
</P>
<P>When Function() is declared to be virtual in the base class, CBase, the code fragment
above would actually call CDerived::Function(), as desired. That's polymorphism,
and that shows up again and again when using MFC classes. You use a pointer to a
window, a CWnd*, that really points to a CButton or a CView or some other class derived
from CWnd, and when a function such as WindowProc() is called, it will be the derived
function--CButton::WindowProc() for example--that is called.
<HR>
<BR>
<HR>
<strong>NOTE:[</strong> You might wonder why the messages can't just be handled by virtual
functions. This would make the virtual tables enormous, and slow the application
too much. The message map system is a much faster approach. n
<HR>
</BLOCKQUOTE>
<P>WindowProc()calls OnWndMsg(), the C++ function that really handles messages. First,
it checks to see whether this is a message, a command, or a notification. Assuming
it's a message, it looks in the message map for the class, using the member variables
and functions set up by DECLARE_MESSAGE_MAP, BEGIN_MESSAGE_MAP, and END_MESSAGE_MAP.
Part of what those macros arrange is to enable access to the message map entries
of the base class by the functions that search the message map of the derived class.
That means that if a class inherits from CView and doesn't catch a message normally
caught by CView, that message will still be caught by the same CView function as
inherited by the derived class. This message map inheritance parallels the C++ inheritance
but is independent of it and saves a lot of trouble carrying virtual functions around.</P>
<P>The bottom line: You add a message map entry, and when a message arrives, the
functions called by the hidden message loop look in these tables to decide which
of your objects, and which member function of the object, should handle the message.
That's what's really going on behind the scenes.</P>
<P>
<H3><A NAME="Heading6"></A>Messages Caught by MFC Code</H3>
<P>The other great advantage of MFC is that the classes already catch most of the
common messages and do the right thing, without any coding on your part at all. For
example, you don't need to catch the message that tells you that the user has chosen
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -