Está en la página 1de 12

How MFC Windows Become Wired

to a WndProc
In the 16-bit version of MFC (version 2.5), MFC registered a single message- handling
procedure called AfxWndProc() for all its windows (except for the non- MFC windows
registered by the developer). As with any other Windows application, an MFC
application’s messages are deposited in a window procedure. In the case of the 16-bit
version of an MFC application, that function was AfxWndProc(). Because the MFC
window classes were registered with AfxWndProc() as the message handler, it was
obvious where messages were handled. Following messages through the system was
easy: you just put a breakpoint in the AfxWndProc() function. Things have changed
significantly with the 32-bit version of MFC. MFC window classes are no longer
registered with the AfxWndProc() function—they’re registered with DefWindowProc()
as the message handler. When you look at the source code for MFC, you’ll find that
AfxWndProc() is still there, and it’s dispatching messages to various CWnd-derived
objects. How is it that messages end up in AfxWndProcO? Remember that MFC
maintains two of its own message hooks: _AfxMsgFilterHook() and
_AfxCbtFilterHook(). Because messages are directed to the hooks before anything else
happens, there’s an opportunity for certain messages to be intercepted in one of these
hooks. As it turns out, MFC uses the computer-based training hook function to attach
AfxWndProcO to the MFC windows as they’re created. Here’s how AfxWndProcO is
hooked up to MFC’s windows.
MFC installs the _AfxCbtFilterHook() function whenever a new CWnd-derived object is
created—that is, during a call to CWnd: :GeateEx(). Right before CWnd: :CreateEx()
makes a call to the CreateWindowEx() API function, CWnd::CreateEx() calls
AfxHookWindowCreate(). AfxHookWindowCreate() inserts _AfxCbtFilterHook() into
the hook chain. Because _AfxCbtFilterHook() is a computer-based training hook,
Windows calls _AfxCbtFilterHook() before activating, creating, destroying, minimizing,
maximizing, moving, or sizing a window. Windows also calls _AfxCbtFilterHook before
completing a system command, before removing a mouse or keyboard event from the
system message queue, before setting the keyboard focus, and before synchronizing with
the system message queue.
_AfxCbtFilterHook() sits there receiving the messages as just described.
_AfxCbtFilterHook() ignores window messages until the HCBT_CREATEWND code is
passed into _AfxCbtFilterHook(). When _AfxCbtFilterHook() is called with the
HCBT_CREATEWND code, that means a window is about to be created.
_AficCbtFilterHookO then calls _AfxStandardSubclass().
_AfxStandardSubclass() uses SetWindowLongO to wire AfxWndProc() up to the
window. From now on, messages for that window will go to AfxWndProc(), where they
are handled by the command-routing architecture. So even though the window was
originally registered with DefWindowProc() as the message-handling procedure, the
framework effectively wires the windows up to AfxWndProc() whenever a CWnd-
derived window is created.
The main reason Microsoft shifted from using AfxWndProc() as the registered window
procedure to using DefWindowProc() is to support 3D Controls, which works through
Microsoft’s CTL3D.DLL. It is desirable to have CTL3D functionality appear to be part
of the system. This is so an app that wants to override CTL3D functionality (in handling
WM_CTLCOLOR messages, primarily) can do so. In order for this to work, MFC has to
ensure that subclassing is in the following order (from first called to last called):
AfxWndProc(), CTL3D’s WndProc(), and finally DefWindowProc(). In order to do this,
Microsoft had to allow CTL3D to subclass before AfxWndProc(), which meant delaying
hooking up AfxWndProc() until after CTL3DSubclassDlgEx is called. So MFC registers
everything with DefWindowProc(), then during _AfxStandardSubclass(), hooks up
CTL3D, then finally adds a subclass on top of CTL3D by installing AfxWndProcO as the
final “top” window procedure. For backward compatibility Microsoft still supports the
idea of registering with AfxWndProcO from the start (you’ll see extra tests in
_AfxStandardSubclass() to handle this).

Handling Messages
Remember that MFC represents windows in two ways: (1) by a unique system- defined
window handle and (2) by the C++ class representing the window. Window handles are
wrapped by CWnd and CWnd-derived classes. It’s easy to get the window handle from a
CWnd object because the window handle is a data member of the class. However, there is
no way to get from the window handle to the CWnd object without some extra footwork.
As you saw earlier, MFC uses a CMapPtrToPtr object to map handles to CWnd objects.
MFC maintains the link for the lifetime of the window. When a window is created using
CWnd (or a CWnd-derived class), the window handle is attached to the CWnd object.
That is, the window handle and the CWnd object are associated through the handle map.
MFC does this so that the framework code can deal with C++ objects rather than window
handles.
And now back to the window procedure. AfxWndProcO handles a single specific
message: WM_QUERYAFXWNDPROC. If the incoming message is
WM_QUERYAFXWNDPROC, AfxWndProc() returns the number 1. Applications can
send the WM_QUERYAFXWNDPROC message to find out if the window is an MFC
window using MFC’s message-mapping system. The return value for the
WM_QUERYAFXWNDPROC message is also used by CWnd’s Dump procedure for
diagnostic information. If the message isn’t WM_QUERYAFXWNDPROC,
AfxWndProc() goes on to process the message.
Inside AfxWndProc(), the framework retrieves the C++ object associated with the
window handle using CWnd::FromHandlePermanent(). The framework then calls
AfxCallWndProc(). Notice that even though the first parameter is a pointer to a CWnd
object, the second parameter is a window handle. This allows AfxCallWndProc() to
maintain a record of the last message processed for use in handling exceptions and
debugging. Here’s the prototype for AfxCallWndProc(). Notice how it looks like any
other window procedure, except that the parameter includes a CWnd pointer as well.
LRESULT PASCAL _AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam);
AfxCallWndProc() first examines the message to see if it’s a WM_INITDIALOG
message, in which case it calls _AfxHandleInitDialog(). _AfxHandleInitDialog() is for
the auto-center dialog feature. MFC caches certain styles before the dialog handles
WM_INITDIALOG. If it is appropriate to center the window (the window is still not
visible and hasn’t moved), then MFC automatically centers the dialog against its parent.
The function AfxCallWndProc() saves the window handle, the message, and the
WPARAM and the LPARAM in the current thread state’s m_lastSentMsg member
variable. Then AfxCallWndProc() simply calls the window object’s window procedure.
Here’s the prototype for WindowProc():
LRESULT CWnd::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam);
Notice that the signature has the same parameters as a regular window procedure. That’s
because at this point, MFC has already mapped the window handle to the existing CWnd-
derived class.
By the way, CWnd::WindowProc() is virtual, so you can override it if you’d like. One
reason you may want to override CWnd::WindowProc() is if you want to handle a
message before MFC even looks at it. For example, you may have a class that bypasses
the message-mapping system—perhaps to improve performance. CWnd::WindowProc()
calls CWnd::OnWndMsg(). If OnWndMsgO returns FALSE, then CWnd::WindowProc()
calls CWnd::DefWindowProc(). The action really begins inside CWnd::OnWndMsg().
Listing 3-3 shows some pared-down source code.

Listing 3-3. The CWnd::OnWndMsg() abbreviated code


BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam,
LRESULT*
pResult) {
if (message == WM_COMMAND)
call OnCommand(wParam, lParam) and return;
if (message == WM_NOTIFY)
call OnNotify(wParam, lParam, slResult) and return;
if (message == WM_ACTIVATE)
call _AfxHandleActivate(this, wParam,
CWnd::FromHandle((HWND)lParam)) and return;
if (message == WM_SETCURSOR) &&
call _AfxHandleSetCursor(this,
(short)LOWORD(lParam),
HIWORD(lParam))) and return;
// Find the message map entry in a message cache using its hash value
if (the message is in the cache ) {
if (message map entry is NULL)
return FALSE;
if (message < OxCOOO) // Registered Windows message
goto LDispatch; // Call the function
else
goto LDispatchRegistered; // Call the function for a registered
// message
• else {
// not in cache, look for it
msgCache.nMsg = message;
msgCache.pMessageMap = pMessageMap;
for (/* pMessageMap already init’ed */; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMap) {
if (message < OxCOOO) {
// constant window message
if ((IpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
message, 0, 0)) != NULL) {
msgCache.IpEntry = IpEntry;
goto LDispatch;
}
} else {
// registered windows message
IpEntry = pMessageMap->lpEntries;
while ((IpEntry = AfxFindMessageEntry(IpEntry, OxCOOO, 0, 0))
1= NULL) {
UINT* pnID = (UINT*)(lpEntry->nSig);
// must be successfully registered
if (*pnID == message) {
msgCache.IpEntry = IpEntry;
goto LDispatchRegistered;
}
lpEntry++; // keep looking past this one
}
}
}
msgCache.IpEntry = NULL;
return FALSE;
}
LDispatch:
ASSERT(message < OxCOOO);
union MessageMapFunctions mmf;
mmf.pfn = lpEntry->pfn;
switch (lpEntry->nSig) {
default:

ASSERT(FALSE);
break;
// Examine the signature type, calling the
// message handler with the appropriate parameters
}
goto LReturnTrue;
LDispatchRegistered: // for registered windows messages ASSERT(message >=
OxCOOO); mmf.pfn = lpEntry->pfn; lResult = (this->*mmf.pfn_lwl)(wParam, lParam);
LReturnTrue:
if (pResult != NULL)
*pResult = lResult;
return TRUE;
}
This is a fairly hefty function. Remember, we’re replacing that unruly switch statement.
Let’s briefly walk through OnWndMsgO before tracing messages through it. First,
OnWndMsgO tries to filter out certain messages from the get-go: WM_COMMAND,
WM_NOTIFY, WM_ACTIVATE, and WM_SETCURSOR. The framework has special
ways of handling each of these messages. If the message isn’t one of those just listed,
OnWndMsgO tries to look up the message in the message map. MFC keeps a message
map entry cache that is accessible via a hash value. This is a great optimization because
looking up a value in a hash table is much cheaper than walking the message map.
This is where commands and regular window messages go their separate ways. If the
message is a command message (that is, nMsg == WM_COMMAND), then
CWnd:.OnWndMsgO calls OnCommand(). Otherwise, it retrieves the window object’s
message map to process the message (more on that shortly). Let’s examine the command
routing first.

Handling WM_COMMAND
The first stop a command makes on its way to its designated command target is
CWnd::OnCommand().
CWnd::OnCommandO
OnCommand() is a virtual function, so the framework calls the correct version. In this
example, because the message was generated for the main frame window, the framework
calls the CFrameWnd version of OnCommand(). BOOL
CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam);
By this point, the message is pared down to just its WPARAM and its LPARAM. If the
message is a request for on-line help, the framework sends a WM_COMMANDHELP
message to the frame window. Otherwise, the message is passed on to the base class’s
OnCommandO, CWnd::OnCommand().
OnCommand() examines the LPARAM, which represents the control. If the command
was generated by a control, then the LPARAM contains the control window. If the
message is a control notification (like LBN_CHANGESEL), then the framework
performs some special processing. If the message is for a control, OnCommandO sends
the last message straight to the control, then OnCommandO returns. Otherwise,
CWnd::OnCommand() makes sure that the user-interface element that generated the
command has not become disabled (for instance, a menu item) and passes the message on
to OnCmdMsgO (which is also virtual). Again, because the frame window is still trying
to handle the message, CFrameWnd: :OnCmdMsg() is the version that’s called. This
function is found in WINFRM.CPP:
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerlnfo);
CFrameWnd::OnCmdMsg() passes NULL for pExtra and pHandlerlnfo when it calls
CFrameWnd::OnCmdMsg(), because this information is not needed for handling
commands.
CFrameWnd::OnCmdMsg() pumps the message through the application components in
this order:
• The active view
• The active view’s document
• The main frame window
• The application
To route the command to the active view, CFrameWnd::OnCmdMsg() tries to find the
frame’s active view using CWnd: :GetActiveView(). If CFrameWnd::OnCmdMsg()
succeeds in finding the frame’s active window, it calls the active view’s OnCmdMsg().
If the active view’s OnCmdMsgO can’t deal with the command, the document gets a
crack at the command. If CFrameWnd::OnCmdMsg() fails to find an active view, or the
view and the document fail to handle the message, then the frame window gets a chance
to handle the message. Finally, if the frame window doesn’t want the message, then the
application takes a crack at the message—CFrameWnd::OnCmdMsg() calls the
application’s OnCmdMsgO function.
At this point in the example, the message has reached the active view and the function
CView::OnCmdMsg(), found in VIEWCORE.CPP:
BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerlnfo);
The framework gives the window pane part of the view a chance to respond to the
message by calling CWnd::OnCmdMsg(). If the view pane can’t handle the message, it’s
pumped through the document.
Because CWnd doesn’t override OnCmdMsgO, the command goes straight to
CCmdTarget::OnCmdMsg(), found in CMDTARG.CPP:
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerlnfo);
CCmdTarget::OnCmdMsgO walks the message map (back to the base class if it has to)
trying to find a handler for the message. If it finds one, it calls that function. If it can’t,
CCmdTarget::OnCmdMsg() returns FALSE, and the document gets a chance to handle
the message. If the document doesn’t want anything to do with the message, then the
message is handled by the CWnd’s DefWindowProc(). If CCmdTarget::OnCmdMsg()
finds a handler in the message map, then it calls DispatchCmdMsgO, also in
CMDTARG.CPP:
static BOOL DispatchCmdMsg(CCmdTarget* pTarget, UINT nID, mt nCode,
AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO*
pHandlerlnfo);
This function is declared static, so it’s visible only within CMDTARG.CPP One of the
parameters is the function signature. This signature comes from the message map entry
itself. (Remember, one of the fields of the message map structure is a function signature.)
Also notice that a pointer to the message handler function (pfn) is passed as a parameter.
This function pointer is also found within the message map entry.
DispatchCmdMsgO switches on the function signature, performing different operations
depending on whether the signature is for a regular command, an extended command, a
command user-interface handler, or a Visual Basic control. In the case of a regular menu
command, the signature is AfxSig_vv (void return, void parameter list).
DispatchCmdMsgO immediately calls the message handler, and— voila—the handler for
that message is called.
If CCmdTarget::OnCmdMsg() fails to find a handler within the message map, it returns
FALSE, which eventually causes CWnd::DefWindowProc() to handle the message.
MFC uses this command-routing scheme for all CCmdTarget-derived classes.
That includes classes derived from CWnd, CDocument, CView, and CFrameWnd. One
interesting aspect of this arrangement is the path that commands take to get to their final
destinations. All command messages take the same path for the first three steps. That is,
the message first lands in AfxWndProc(), which gets the CWnd object from the HWND
parameter and calls AfxCallWndProc(). AfxCallWndProc() calls the CWnd-derived
object’s WindowProc(). From there, the message is routed to its intended destination.
Here’s a rundown of the path a command message takes to the various components of an
MFC application.

Command to a Frame Window


Here is the path a WM_COMMAND message takes to an application’s frame window.
As with all Windows messages through an MFC program, the first stop is AfxWndProc().
This calls AfxCallWndProc(), finally ending up in the specific window’s window
procedure. From there, the command message is routed to the appropriate command
target.
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CCmdTarget::OnCmdMsg()
DispatchCmdMsg()
CMainFrame::OnFrameAfamecommand()

Command to a Document
Here is the path a WM_COMMAND message takes to an application’s document:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CView::OnCmdMsg()
CDocument::OnCmdMsg()
CCmdTarget::OnCmdMsg()
DispatchCmdMsg()
CSdiappDoc::OnDoccommandAdoccommand()
Command to a View
Here is the path a WM_COMMAND message takes to an application’s view:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CView::OnCmdMsg()
CCmdTarget::OnCmdMsg()
DispatchCmdMsg()
CSdiappView::OnViewAviewcommand()
Command to an App
Here is the path a WM_COMMAND message takes to an application’s CWinApp-
derived object:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CCmdTarget::OnCmdMsg()
DispatchCmdMsg()
CSdiappApp::OnAppAnappcommand()
Command to a Dialog Box
Dialog boxes also receive command messages. Here is the path a WM_COMMAND
message takes to a dialog box:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CWnd::OnCommand()
CDialog::OnCmdMsg()
CCmdTarget::OnCmdMsg()
DispatchCmdMsg()
CAboutDlg::OnAButton()
So that’s how command messages come through the framework. As you can see, the
message goes caroming between several different classes. Handling regular window
messages (like WM_SIZE) is quite a bit simpler.

Handling Regular Window Messages


As with command messages, a regular message starts out in AfxWndProc(), which goes
on to turn the window handle into a CWnd object. AfxWndProcO then calls
AfxCallWndProcO, which in turn calls the CWnd object’s WindowProc().
WindowProc() then calls OnWndMsgO, which calls AfxFindMessageEntryO to find a
handler for the message.

AfxFindMessageEntryO
AfxFindMessageEntryO is an interesting function. There are actually two versions of it:
one written in assembly language and one written in C. If the application is compiled for
an Intel-based machine, then AfxFindMessageEntryO uses the assembly version.
Otherwise, AfxFindMessageEntryO uses the C version. To find the message map entry
in the table, OnWndMsgO first retrieves the CWnd object’s message map. Then, given
the first entry in that message map, AfxFindMessageEntryO walks the array of message
map entries until it either (a) finds the message in the message map or (b) finds the end of
the message map as marked by the AfxSig_end signature (remember, that’s what the
END_MESSAGE_MAP macro does).
When OnWndMsgO finds a handler, it calls the handler. If the message map doesn’t
include a handler for a specific message, the framework calls the window’s default
window procedure. So, unlike commands, which are routed to several places, regular
window messages go straight to the window for which they were intended. For example,
take a look at the path a WM_SIZE message takes to its destination (a CWnd-derived
class).

Sending a WMSIZE Message to a View


Here is the path that a WM_SIZE message takes to an application’s view. Notice how
window messages make a beeline to their CWnd-derived objects. AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CSdiappView::OnSize()
Calling the Member Function
Before leaving message maps, let’s take a look at how MFC calls the member function
for a particular window message once it finds the entry in the message map. Recall
that one of the members of the message map entry structure is a pointer to the
function handling the window message. To deal with this, OnWndMsgO declares a
MessageMapFunctions union on the stack. Listing 3-4 shows a portion of the union:
Listing 3-4. Signatures for MFC’s message-handler functions
union MessageMapFunctions
{
AFX_PMSG pfn; // generic member function pointer
// specific type safe variants
BOOL (AFX_MSG_CALL CWnd::*pfn_bD)(CDC*);
BOOL (AFX_MSG_CALL CWnd::*pfn_bb)(BOOL);
BOOL (AFX_MSG_CALL CWnd::*pfn_bWww)(CWnd*, UINT, UINT);
BOOL (AFX_MSG_CALL CWnd::*pfn_bHELPINFO)(HELPINFO*);
HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDWw)(CDC*, CWnd*, UINT);
HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDw)(CDC*, UINT); int
(AFX_MSG_CALL CWnd::*pfn_iwWw)(UINT, CWnd*, UINT); int
(AFX_MSG_CALL CWnd: :*pfn__iww) (UINT, UINT); int (AFX_MSG_CALL
CWnd::*pfn_iWww)(CWnd*, UINT, UINT); int (AFX_MSG_CALL CWnd::*pfn_is)
(LPTSTR);
LRESULT (AFX_MSG_CALL CWnd::*pfn_lwl)(WPARAM, LPARAM);
LRESULT (AFX_MSG_CALL CWnd::*pfn_lwwM)(UINT, UINT, CMenu*); void
(AFX_MSG_CALL CWnd::*pfn_vv)(void); void (AFX_MSG_CALL CWnd::*pfn_vw)
(UINT); void (AFX__MSG_CALL CWnd: :*pfn_vww) (UINT, UINT); void
(AFX_MSG__CALL CWnd: :*pfn_vwii) (UINT, int, int);
};
The MessageMapFunctions union contains a generic AFX function pointer as a member,
followed bv prototypes for all the different kinds of functions that might be used for a
message handler. OnWndMsgO sets the MessageMapFunctions union to the message
handler’s address: mmf.pfn = lpEntry->pfn;
Next, OnWndMsgO goes on to find the signature that matches the signature for a
WM_SIZE message. Once OnWndMsgO finds a match, it pulls the necessary parameters
from the WPARAM and the LPARAM and calls the handler using the prototype
included in the MessageMapFunctions structure. For example, here’s the line of code
that executes a handler for WM_SIZE. case AfxSig_vwii:
(this->*mmf.pfn_vwii)(wParam, LOWORD(lParam), HIWORD(lParam)); break;
Pretty slick, huh? Some have said looking at MFC is kind of like touring a sausage
factory. The sausages taste good and work fine (as far as sausages go), but you really
should never look at how they make the sausages. MFC’s message-mapping code is
sometimes a little hard to follow. However, it does work very well and it is an important
component of Visual C++’s Wizard technology. In addition to WM__COMMAND and
the regular window messages, MFC handles certain other window messages in special
ways.

Other Kinds of Messages


The WM_COMMAND and various window messages (like WM_SIZE and
WM_MOVE) are the most common kinds of messages your app will receive. However,
MFC has special ways to handle the WM_NOTIFY, the WM_ACTIVATE, and the
WM_SETCURSOR messages. Let’s take a brief look at these special cases.

WM_NOTIFY and Message Reflection


In earlier versions of Windows, WM_COMMAND was an overloaded message that
could represent (1) a command from a menu item or (2) a notification from a child
window. With the advent of Windows 95 and the new common controls, there’s a new
message called WM_NOTIFY. WM_NOTIFY is a generalized control notification. Wl^
(_NOTIFY is always a notification, whereas WM_COMMAND can be either a command
or a notification.
CWnd: :OnWndMsgO handles WM_NOTIFY specifically through the CWnd:
:OnNotifyO member function. Instead of passing the regular WPARAM and LPARAM
arguments, Wivi.NOTIFY packs a NMHDR structure in the LPARAM:
struct NMHDR {
HWND hwndFrom; // control that sent notification
UINT idFrom; // ID of control
UINT code; // notification code
>;
OnNotifyO uses the hwndFrom field and ends up calling the virtual CWnd function
OnChildNotify(). OnChildNotifyO then sends the message directly to the control so that
the control can deal with it (after all, it really is the control’s responsibility). In addition
to handling WM_NOTIFY for the new controls, MFC provides a new mechanism called
message reflection. Recall how control notifications worked in earlier versions of MFC:
Whenever something interesting happened to the control, the control notified its parent
so that the parent could do something about it. A good example is coloring a control in a
dialog box. You could plant regular controls in a dialog box. However, if you wanted to
change their colors, it was the dialog’s responsibility to respond to the WM_CTLCOLOR
message, coloring the controls as appropriate. Of course, this isn’t the best way to do this
sort of thing because it’s not very self-contained. In an ideal world, the control would be
responsible for painting itself. Message reflection provides this capability in MFC 4.0.
MFC defines some new message map macros for handling reflected messages. They are
similar to the macros for the standard message map macros except that they have the
label “REFLECT’ attached to them. For example, the standard handler for the
WM.CTLCOLOR message is ON_WM_CTLCOLOR. The ON_WM_CTLCOLOR takes
a function that takes a pointer to a CDC and an unsigned integer as parameters and
returns an HBRUSH. Adding this macro to the message map causes the control to be
notified whenever the parent receives the WM_CTLCOLOR message. That way, the
control can handle the message rather than depend on the parent to deal with the
repainting.

WM_ACTIVATE
MFC’s messaging architecture also handles WM_ACTIVATE. OnWndMsgO calls the
non-C++ member function _AfxHandleActivate(). This function checks to see if the
WM_AOTVATE message is for the top-level window. If it is, _AfxHandleActivate()
sends the WM_ACTIVATETOPLEVEL message to the top-level window.

WM_SETCURSOR
MFC handles the WM_SETCURSOR message within the function
_AixHandleSetCursorQ. _AfxHandleSetCursor() checks to see if one of the mouse
buttons is pressed. If a mouse button is down, _AfxHandleSetCursor() activates the last
active window. This is necessary to work around a Windows bug. If a window is disabled
and a modal dialog is parented to that disabled window, the app will not activate itself
correctly when the user clicks on the disabled window. Instead, the user must click
directly on the dialog itself. If the dialog is small, it is probably covered. Normally,
clicking on the disabled window would result in just a beep from DefWindowProc() in
this circumstance. MFC fixes this Windows bug by activating the dialog box (and thus
bringing the app to the top) when you click on a disabled window with an active modal
dialog box (or other modal window).
Hooking into the Message Loop:

PreTranslateMessageQ
MFC has another useful feature: you can plug into the message loop to handle messages
before they ever get to their designated command targets or windows. The function for
doing that is PreTranslateMessage(). MFC gives you two places where you can hook into
the message loop: CWinApp::PreTranslateMessageO and CWnd::PreTranslateMessageO.
CWinApp::PreTranslateMessage() lets you process window messages even before they
get to any of your application’s windows or command targets. CWinApp::Run() calls
CWinApp::PreTranslateMessage() before the message is processed by
TranslateMessageO and DispatchMessage(). CWndApp: :PreTranslateMessage() takes a
single parameter: a pointer to an MSG structure. By default, the framework examines the
message. If the message is a mouse button down or a mouse button double-click,
CWndApp ::PreTranslateMessage() removes any tool tips from the screen by calling
CancelToolTip().
Then CWinApp::PreTranslateMessage() calls each of the windows from the target
window (as designated by the window handle in the message structure) to the
application’s main window. CWnd::WalkPreTranslateTree() performs this function, as
shown in Listing 3-5.

Listing 3-5. CWnd’s WalkPreTranslateTree() function


BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
{
ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));
ASSERT(pMsg 1= NULL);
// walk from the target window up to the hWndStop window checking // if any window
wants to translate this message for (HWND hWnd = pMsg->hwnd; hWnd != NULL;
hWnd = ::GetParent(hWnd))
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); if (pWnd 1= NULL)
{
// target window is a C++ window
if (pWnd->PreTranslateMessage(pMsg))
return TRUE; // trapped by target window (eg: accelerators)
}
// got to hWndStop window without interest
if (hWnd == hWndStop)
break;
}
return FALSE; //no special processing
}
Before calling WalkPreTranslateTreeO, CWinApp::PreTranslateMessage() retrieves the
window handle of the application’s main window by calling AfxGetMainWnd().
WalkPreTranslateTreeO uses this handle to know when to stop calling
PreTranslateMessage(). Then starting inside-out (that is, starting from the target
window), WalkPreTranslateTreeO hits each window, trying to find one interested in the
message. For each window in the tree, WalkPreTranslateTreeO fetches the CWnd object
from the window handle map, calling PreTranslateMessage() for each CWnd object.
WalkPreTranslateTreeO does this for each window until either (1) the window handles
the message or (2) WalkPreTranslateTreeO reaches the application’s main window.
CWnd::PreTranslateMessage() takes a single parameter: a pointer to the MSG structure.
When you override PreTranslateMessage(), just examine the MSG structure. If you’re
interested in the message, go ahead and handle it within PreTranslateMessage() and
return TRUE.
CWinApp::PreTranslateMessage() returns TRUE if it finds a window interested in the
message. If CWinApp::PreTranslateMessage() returns TRUE, then CWinApp’s message
pump doesn’t dispatch the message. So, PreTranslateMessage() effectively eats the
message.

También podría gustarte