📄 ch03.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!--
function popUp(pPage) {
var fullURL = document.location;
var textURL = fullURL.toString();
var URLlen = textURL.length;
var lenMinusPage = textURL.lastIndexOf("/");
lenMinusPage += 1;
var fullPath = textURL.substring(0,lenMinusPage);
popUpWin = window.open('','popWin','resizable=yes,scrollbars=no,width=525,height=394');
figDoc= popUpWin.document;
zhtm= '<HTML><HEAD><TITLE>' + pPage + '</TITLE>';
zhtm += '<link rel="stylesheet" href="/includes/stylesheets/ebooks.css"></head>';
zhtm += '<BODY bgcolor="#FFFFFF">';
zhtm += '<IMG SRC="' + fullPath + pPage + '">';
zhtm += '<P><B>' + pPage + '</B>';
zhtm += '</BODY></HTML>';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}
//-->
</SCRIPT>
<link rel="stylesheet" href="/includes/stylesheets/ebooks.css">
<TITLE>Special Edition Using Visual C++ 6 -- Ch 3 -- Messages and Commands</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF">
<CENTER>
<H1><IMG SRC="../button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>
Special Edition Using Visual C++ 6</H1>
</CENTER>
<CENTER>
<P><A HREF="../ch02/ch02.htm"><IMG SRC="../button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="../ch04/ch04.htm"><IMG
SRC="../button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A>
<HR>
</CENTER>
<CENTER>
<H1>- 3 -</H1>
<H1>Messages and Commands</H1>
</CENTER>
<UL>
<LI><A HREF="#Heading1">Understanding Message Routing</A>
<LI><A HREF="#Heading2">Understanding Message Loops</A>
<LI><A HREF="#Heading3">Reading Message Maps</A>
<UL>
<LI><A HREF="#Heading4">Message Map Macros</A>
<LI><A HREF="#Heading5">How Message Maps Work</A>
<LI><A HREF="#Heading6">Messages Caught by MFC Code</A>
</UL>
<LI><A HREF="#Heading7">Learning How ClassWizard Helps You Catch Messages</A>
<UL>
<LI><A HREF="#Heading8">The ClassWizard Tabbed Dialog Box</A>
<LI><A HREF="#Heading9">The Add Windows Message Handler Dialog Box</A>
<LI><A HREF="#Heading10">Which Class Should Catch the Message?</A>
</UL>
<LI><A HREF="#Heading11">Recognizing Messages</A>
<LI><A HREF="#Heading12">Understanding Commands</A>
<LI><A HREF="#Heading13">Understanding Command Updates</A>
<LI><A HREF="#Heading14">Learning How ClassWizard Helps You Catch Commands and Command
Updates</A>
</UL>
<P>
<HR SIZE="4">
<CENTER>
<H1></H1>
</CENTER>
<H2><A NAME="Heading1"></A>Understanding Message Routing</H2>
<P>If there is one thing that sets Windows programming apart from other kinds of
programming, it is messages. Most DOS programs, for example, relied on watching (sometimes
called <I>polling</I>) possible sources of input like the keyboard or the mouse to
await input from them. A program that wasn't polling the mouse would not react to
mouse input. In contrast, everything that happens in a Windows program is mediated
by messages. A message is a way for the operating system to tell an application that
something has happened--for example, the user has typed, clicked, or moved the mouse,
or the printer has become available. A window (and every screen element is a window)
can also send a message to another window, and typically most windows react to messages
by passing a slightly different message along to another window. MFC has made it
much easier to deal with messages, but you must understand what is going on beneath
the surface.</P>
<P>Messages are all referred to by their names, though the operating system uses
integers to refer to them. An enormous list of #define statements connects names
to numbers and lets Windows programmers talk about WM_PAINT or WM_SIZE or whatever
message they need to talk about. (The WM stands for Window Message.) An excerpt from
that list is shown in Listing 3.1.</P>
<P>
<H4>Listing 3.1  Excerpt from winuser.h Defining Message Names</H4>
<PRE>#define WM_SETFOCUS 0x0007
#define WM_KILLFOCUS 0x0008
#define WM_ENABLE 0x000A
#define WM_SETREDRAW 0x000B
#define WM_SETTEXT 0x000C
#define WM_GETTEXT 0x000D
#define WM_GETTEXTLENGTH 0x000E
#define WM_PAINT 0x000F
#define WM_CLOSE 0x0010
#define WM_QUERYENDSESSION 0x0011
#define WM_QUIT 0x0012
#define WM_QUERYOPEN 0x0013
#define WM_ERASEBKGND 0x0014
#define WM_SYSCOLORCHANGE 0x0015
</PRE>
<PRE>#define WM_ENDSESSION 0x0016
</PRE>
<P>As well as a name, a message knows what window it is for and can have up to two
parameters. (Often, several different values are packed into these parameters, but
that's another story.)</P>
<P>Different messages are handled by different parts of the operating system or your
application. For example, when the user moves the mouse over a window, the window
receives a WM_MOUSEMOVE message, which it almost certainly passes to the operating
system to deal with. The operating system redraws the mouse cursor at the new location.
When the left button is clicked over a button, the button (which is a window) receives
a WM_LBUTTONDOWN message and handles it, often generating another message to the
window that contains the button, saying, in effect, "I was clicked."</P>
<P>MFC has enabled many programmers to completely ignore low-level messages such
as WM_MOUSEMOVE and WM_LBUTTONDOWN. Instead, programmers deal only with higher level
messages that mean things like "The third item in this list box has been selected"
or "The Submit button has been clicked." All these kinds of messages move
around in your code and the operating system code in the same way as the lower level
messages. The only difference is what piece of code chooses to handle them. MFC makes
it much simpler to announce, at the individual class's level, which messages each
class can handle. The old C way, which you will see in the next section, made those
announcements at a higher level and interfered with the object-oriented approach
to Windows programming, which involves hiding implementation details as much as possible
inside objects.</P>
<P>
<H2><A NAME="Heading2"></A>Understanding Message Loops</H2>
<P>The heart of any Windows program is the message loop, typically contained in a
WinMain() routine. The WinMain() routine is, like the main() in DOS or UNIX, the
function called by the operating system when you run the program. You won't write
any WinMain() routines because it is now hidden away in the code that AppWizard generates
for you. Still, there is a WinMain(), just as there is in Windows C programs. Listing
3.2 shows a typical WinMain().</P>
<P>
<H4>Listing 3.2  Typical WinMain() Routine</H4>
<PRE>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);
</PRE>
<PRE>}
</PRE>
<P>In a Windows C program like this, InitApplication() typically calls RegisterWindow(),
and InitInstance() typically calls CreateWindow(). (More details on this are in Appendix
B, "Windows Programming Review and a Look Inside Cwnd.") Then comes the
message loop, the while loop that calls GetMessage(). The API function GetMessage()
fills msg with a message destined for this application and almost always returns
TRUE, so this loop runs over and over until the program is finished. The only thing
that makes GetMessage() return FALSE is if the message it receives is WM_QUIT.</P>
<P>TranslateMessage() is an API function that streamlines dealing with keyboard messages.
Most of the time, you don't need to know that "the A key just went down"
or "the A key just went up," and so on. It's enough to know that "the
user pressed A." TranslateMessage() deals with that. It catches the WM_KEYDOWN
and WM_KEYUP messages and usually sends a WM_CHAR message in their place. Of course,
with MFC, most of the time you don't care that the user pressed A. The user types
into an edit box or similar control, and you can retrieve the entire string out of
it later, when the user has clicked OK. Don't worry too much about TranslateMessage().</P>
<P>The API function DispatchMessage() calls the WndProc for the window that the message
is headed for. The WndProc() function for a Windows C program is a huge switch statement
with one case for each message the programmer planned to catch, such as the one in
Listing 3.3.</P>
<P>
<H4>Listing 3.3  Typical WndProc() Routine</H4>
<PRE>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);
</PRE>
<PRE>}
</PRE>
<P>As you can imagine, these WndProcs become very long in a hurry. Program maintenance
can be a nightmare. MFC solves this problem by keeping information about message
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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -