不管。我现在要仿真出消息的流动循环路线--我常喜欢称之为消息的“二万五千里长征”。
消息如果是从子类流向父类(纵向流动),那么事情再简单不过,整个Message Map消息映射表已规划出十分明确的路线。但是正如卜一节一开始我说的,MFC之中用来处
理消息的C++类并不旱单线发展,作为application framework的重要结构之一的document/view,也具有处理消息的能力(你现在可能还不清楚什么是document/view,没
有关系),因此,消息应该有横向流动的机会。MFC对于消息循环的规定是:
1)如果是一般的Window、消息(WM xxx,则一定是由派生类流向基类, 没有旁流的可能。
2) 如果是命令消息WM COMMAND,那就有奇特的路线了:
图(1)MFC对于命令消息WM_COMMAND的特殊处理
不管这个规则是怎么定下来的,现在我要设计一个推动引擎,把它仿真出来。以下这些函数名称以及函数内容,完全仿真MFC内部。有些函数似乎赘余,那是因为我删掉了
许多卞题以外的操作。不把看似赘余的函数拿掉或合并,是为了留下MFC的足迹。此外,为了追踪调用过程(call stack),我在各函数的第一行输出一串识别文字。
首先我把新增加的一些成员函数做个列表:
全局函数AfxWhdPrn。就是我所谓的推动引擎的起始点。它本来应该是在CWihThread::Ruh中被调用,但为了实验目的,我在main中调用它,每调用一次便推送
一个消息。这个函数在1V}'C中有四个参数,为了方便,我加卜第五个,用以表示是谁获得消息(成为循环的起点)。例如:
AfxWndProc(0,WM CREATE,0,0,pMyFrame);
表示pM夕Fram。获得了一个WM_CREATE,而:
AfxWndProc(0,WM COMMAND,0,0,pMyView);
表示pMyView获得了一个WM_COMMAND
下面是消息的传递过程:
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
CWnd *pWnd) // last param. pWnd is added by JJHou.
{
cout << "AfxWndProc()" << endl;
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam)
{
cout << "AfxCallWndProc()" << endl;
LRESULT lResult = pWnd->WindowProc(nMsg, wParam, lParam);
return lResult;
}
pWhd->WihdnwProc。究竟是调用哪一个函数?不一定,得视pW}d到底指向何种类之对象而定—别忘了WihdnwPrnc是虚函数。这正是虚函数发挥它功效的地方呀:
a)如果pWi}d指向CMyFrameWhd对象,那么调用的是CFrameWhd::WihdnwProc
而因为CFrameWhd并没有改写WihdnwProc,所以调用的其实是
CWhd:: WihdnwProc。
b)如果pWnd指向CMyView对象,那么调用的是CView:: Wi}dnwProc。而因为 CView并没有改写WihdnwProc,所以调用的其实是CWhd:: WihdnwProc
虽然殊途同归,但意义却是不相同的。切记!切记!
CWhd::WihdnwPrn。首先判断消息是否为WM COMMAND。如果不是,事情最单纯,就把消息往父类推去,父类再往祖父类推去。每到一个类的消息映射表,原本应该比较
AFX_MSGMAP_ENTRY的每一个元素,比较成功就调用对应的处理程序。
不过在这里我不作比较,只是把AFX_MSGMAP_ENTRY中的类识别代码印出来(就像上一节的Frame7程序一样),以表示“到此一游”:
如果消息是WM COMMAND } CWhd::WihdnwPrn。调用} OhCnmmahd。好,注意这又是一个CW}d的虚函数:
1.如果thi、指向CMyFrameWhd对象,那么调用的是CFrameWhd::OhCnmmahd
2.如果thi、指向CMyView对象,那么调用的是CView::OhCnmmahd。而因为
CView并没有改写OhCnmmahd,所以调用的其实是CWhd: :OhCnmmahd,这次可就没有殊途同归了。
消息的传递路线,如下:
Frame8测试四种情况:分别从frame对象和view 对象中推动消息,消息一般分为 Windows消息和 WM_COMMADND两种:
// test Message Routing
AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);
AfxWndProc(0, WM_PAINT, 0, 0, pMyView);
AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);
AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);
效果如下:
CWinApp::InitApplication
CMyWinApp::InitInstance
CFrameWnd::Create
CWnd::CreateEx
CFrameWnd::PreCreateWindow
CWinApp::Run
CWinThread::Run
pMyFrame receive WM_CREATE, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
1211 CMyFrameWnd
121 CFrameWnd
12 CWnd
1 CCmdTarget
pMyView receive WM_PAINT, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
1221 CMyView
122 CView
12 CWnd
1 CCmdTarget
pMyView receive WM_COMMAND, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnCommand()
CView::OnCmdMsg()
CCmdTarget::OnCmdMsg()
1221 CMyView
122 CView
12 CWnd
1 CCmdTarget
CDocument::OnCmdMsg()
CCmdTarget::OnCmdMsg()
131 CMyDoc
13 CDocument
1 CCmdTarget
CWnd::DefWindowProc()
pMyFrame receive WM_COMMAND, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CFrameWnd::GetActiveView()
CView::OnCmdMsg()
CCmdTarget::OnCmdMsg()
1221 CMyView
122 CView
12 CWnd
1 CCmdTarget
CDocument::OnCmdMsg()
CCmdTarget::OnCmdMsg()
131 CMyDoc
13 CDocument
1 CCmdTarget
CCmdTarget::OnCmdMsg()
1211 CMyFrameWnd
121 CFrameWnd
12 CWnd
1 CCmdTarget
CCmdTarget::OnCmdMsg()
1111 CMyWinApp
111 CWinApp
1 CCmdTarget
CWnd::DefWindowProc()
Press any key to continue
控制台应用程序Frame8的详细代码,如下:
//mfc.h
#define TRUE 1
#define FALSE 0
typedef char* LPSTR;
typedef const char* LPCSTR;
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef int INT;
typedef unsigned int UINT;
typedef long LONG;
typedef UINT WPARAM;
typedef LONG LPARAM;
typedef LONG LRESULT;
typedef int HWND;
#define WM_COMMAND 0x0111 // following windows.h
#define WM_CREATE 0x0001
#define WM_PAINT 0x000F
#define WM_NOTIFY 0x004E
#define CObjectid 0xffff
#define CCmdTargetid 1
#define CWinThreadid 11
#define CWinAppid 111
#define CMyWinAppid 1111
#define CWndid 12
#define CFrameWndid 121
#define CMyFrameWndid 1211
#define CViewid 122
#define CMyViewid 1221
#define CDocumentid 13
#define CMyDocid 131
#include <iostream.h>
// Window message map handling
struct AFX_MSGMAP_ENTRY;
struct AFX_MSGMAP
{
AFX_MSGMAP* pBaseMessageMap;
AFX_MSGMAP_ENTRY* lpEntries;
};
#define DECLARE_MESSAGE_MAP() \
static AFX_MSGMAP_ENTRY _messageEntries[]; \
static AFX_MSGMAP messageMap; \
virtual AFX_MSGMAP* GetMessageMap() const;
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_MSGMAP theClass::messageMap = \
{ &(baseClass::messageMap), \
(AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) }; \
AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{
#define END_MESSAGE_MAP() \
{ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
};
// Message map signature values and macros in separate header
#include "afxmsg_.h"
class CObject
{
public:
CObject::CObject() {
}
CObject::~CObject() {
}
};
class CCmdTarget : public CObject
{
public:
CCmdTarget::CCmdTarget() {
}
CCmdTarget::~CCmdTarget() {
}
virtual BOOL OnCmdMsg(UINT nID, int nCode);
DECLARE_MESSAGE_MAP() // base class - no {{ }} macros
};
typedef void (CCmdTarget::*AFX_PMSG)(void);
struct AFX_MSGMAP_ENTRY // MFC 4.0 format
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
class CWinThread : public CCmdTarget
{
public:
CWinThread::CWinThread() {
}
CWinThread::~CWinThread() {
}
virtual BOOL InitInstance() {
cout << "CWinThread::InitInstance \n";
return TRUE;
}
virtual int Run() {
cout << "CWinThread::Run \n";
// AfxWndProc(...);
return 1;
}
};
class CWnd;
class CWinApp : public CWinThread
{
public:
CWinApp* m_pCurrentWinApp;
CWnd* m_pMainWnd;
public:
CWinApp::CWinApp() {
m_pCurrentWinApp = this;
}
CWinApp::~CWinApp() {
}
virtual BOOL InitApplication() {
cout << "CWinApp::InitApplication \n";
return TRUE;
}
virtual BOOL InitInstance() {
cout << "CWinApp::InitInstance \n";
return TRUE;
}
virtual int Run() {
cout << "CWinApp::Run \n";
return CWinThread::Run();
}
DECLARE_MESSAGE_MAP()
};
typedef void (CWnd::*AFX_PMSGW)(void);
// like 'AFX_PMSG' but for CWnd derived classes only
class CDocument : public CCmdTarget
{
public:
CDocument::CDocument() {
}
CDocument::~CDocument() {
}
virtual BOOL OnCmdMsg(UINT nID, int nCode);
DECLARE_MESSAGE_MAP()
};
class CWnd : public CCmdTarget
{
public:
CWnd::CWnd() {
}
CWnd::~CWnd() {
}
virtual BOOL Create();
BOOL CreateEx();
virtual BOOL PreCreateWindow();
virtual LRESULT WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam);
virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
class CView;
class CFrameWnd : public CWnd
{
public:
CView* m_pViewActive; // current active view
public:
CFrameWnd::CFrameWnd() {
}
CFrameWnd::~CFrameWnd() {
}
BOOL Create();
virtual BOOL PreCreateWindow();
CView* GetActiveView() const;
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnCmdMsg(UINT nID, int nCode);
DECLARE_MESSAGE_MAP()
friend CView;
};
class CView : public CWnd
{
public:
CDocument* m_pDocument;
public:
CView::CView() {
}
CView::~CView() {
}
virtual BOOL OnCmdMsg(UINT nID, int nCode);
DECLARE_MESSAGE_MAP()
friend CFrameWnd;
};
// global function
CWinApp* AfxGetApp();
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
CWnd* pWnd); // last param. pWnd is added by JJHOU.
LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam,
LPARAM lParam);
//mfc.cpp
#include "my.h"
extern CMyWinApp theApp;
extern void printlpEntries(AFX_MSGMAP_ENTRY* lpEntry);
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode)
{
cout << "CCmdTarget::OnCmdMsg()" << endl;
// Now look through message map to see if it applies to us
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
for (pMessageMap = GetMessageMap(); pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
return FALSE; // not handled
}
BOOL CWnd::Create()
{
cout << "CWnd::Create \n";
return TRUE;
}
BOOL CWnd::CreateEx()
{
cout << "CWnd::CreateEx \n";
PreCreateWindow();
return TRUE;
}
BOOL CWnd::PreCreateWindow()
{
cout << "CWnd::PreCreateWindow \n";
return TRUE;
}
LRESULT CWnd::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
cout << "CWnd::WindowProc()" << endl;
if (nMsg == WM_COMMAND) // special case for commands
{
if (OnCommand(wParam, lParam))
return 1L; // command handled
else
return (LRESULT)DefWindowProc(nMsg, wParam, lParam);
}
pMessageMap = GetMessageMap();
for (; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
return 0; // J.J.Hou: if find, should call lpEntry->pfn,
// otherwise should call DefWindowProc.
// for simplization we just return 0.
}
LRESULT CWnd::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
cout << "CWnd::DefWindowProc()" << endl;
return TRUE;
}
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
cout << "CWnd::OnCommand()" << endl;
// ...
return OnCmdMsg(0, 0);
}
BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
cout << "CFrameWnd::OnCommand()" << endl;
// ...
// route as normal command
return CWnd::OnCommand(wParam, lParam);
}
BOOL CFrameWnd::Create()
{
cout << "CFrameWnd::Create \n";
CreateEx();
return TRUE;
}
BOOL CFrameWnd::PreCreateWindow()
{
cout << "CFrameWnd::PreCreateWindow \n";
return TRUE;
}
CView* CFrameWnd::GetActiveView() const
{
cout << "CFrameWnd::GetActiveView()" << endl;
return m_pViewActive;
}
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode)
{
cout << "CFrameWnd::OnCmdMsg()" << endl;
// pump through current view FIRST
CView* pView = GetActiveView();
if (pView->OnCmdMsg(nID, nCode))
return TRUE;
// then pump through frame
if (CWnd::OnCmdMsg(nID, nCode))
return TRUE;
// last but not least, pump through app
CWinApp* pApp = AfxGetApp();
if (pApp->OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
BOOL CDocument::OnCmdMsg(UINT nID, int nCode)
{
cout << "CDocument::OnCmdMsg()" << endl;
if (CCmdTarget::OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
BOOL CView::OnCmdMsg(UINT nID, int nCode)
{
cout << "CView::OnCmdMsg()" << endl;
if (CWnd::OnCmdMsg(nID, nCode))
return TRUE;
BOOL bHandled = FALSE;
bHandled = m_pDocument->OnCmdMsg(nID, nCode);
return bHandled;
}
AFX_MSGMAP* CCmdTarget::GetMessageMap() const // JJHou: in MFC 40 cmdtarg.cpp
{
return &CCmdTarget::messageMap;
}
AFX_MSGMAP CCmdTarget::messageMap = // JJHou: in in MFC 40 cmdtarg.cpp
{
NULL,
&CCmdTarget::_messageEntries[0]
};
AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] = // JJHou: in in MFC 40 cmdtarg.cpp
{
{ 0, 0, CCmdTargetid, 0, AfxSig_end, 0 } // nothing here
};
BEGIN_MESSAGE_MAP(CWnd, CCmdTarget)
ON_COMMAND(CWndid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CFrameWnd, CWnd)
ON_COMMAND(CFrameWndid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
ON_COMMAND(CDocumentid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CView, CWnd)
ON_COMMAND(CViewid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget)
ON_COMMAND(CWinAppid, 0)
END_MESSAGE_MAP()
CWinApp* AfxGetApp()
{
return theApp.m_pCurrentWinApp;
}
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
CWnd *pWnd) // last param. pWnd is added by JJHou.
{
cout << "AfxWndProc()" << endl;
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam)
{
cout << "AfxCallWndProc()" << endl;
LRESULT lResult = pWnd->WindowProc(nMsg, wParam, lParam);
return lResult;
}
//my.h
#include <iostream.h>
#include "mfc.h"
class CMyWinApp : public CWinApp
{
public:
CMyWinApp::CMyWinApp() {
}
CMyWinApp::~CMyWinApp() {
}
virtual BOOL InitInstance();
DECLARE_MESSAGE_MAP()
};
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd();
~CMyFrameWnd() {
}
DECLARE_MESSAGE_MAP()
};
class CMyDoc : public CDocument
{
public:
CMyDoc::CMyDoc() {
}
CMyDoc::~CMyDoc() {
}
DECLARE_MESSAGE_MAP()
};
class CMyView : public CView
{
public:
CMyView::CMyView() {
}
CMyView::~CMyView() {
}
DECLARE_MESSAGE_MAP()
};
//my.cpp
#include "my.h"
CMyWinApp theApp; // global object
BOOL CMyWinApp::InitInstance()
{
cout << "CMyWinApp::InitInstance \n";
m_pMainWnd = new CMyFrameWnd;
return TRUE;
}
CMyFrameWnd::CMyFrameWnd()
{
Create();
}
BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
ON_COMMAND(CMyWinAppid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(CMyFrameWndid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(CMyDocid, 0)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(CMyViewid, 0)
END_MESSAGE_MAP()
void printlpEntries(AFX_MSGMAP_ENTRY* lpEntry)
{
struct {
int classid;
char* classname;
} classinfo[] = {
CCmdTargetid , "CCmdTarget ",
CWinThreadid , "CWinThread ",
CWinAppid , "CWinApp ",
CMyWinAppid , "CMyWinApp ",
CWndid , "CWnd ",
CFrameWndid , "CFrameWnd ",
CMyFrameWndid, "CMyFrameWnd ",
CViewid , "CView ",
CMyViewid , "CMyView ",
CDocumentid , "CDocument ",
CMyDocid , "CMyDoc ",
0 , " "
};
for (int i=0; classinfo[i].classid != 0; i++)
{
if (classinfo[i].classid == lpEntry->nID)
{
cout << lpEntry->nID << " ";
cout << classinfo[i].classname << endl;
break;
}
}
}
//------------------------------------------------------------------
// main
//------------------------------------------------------------------
void main()
{
CWinApp* pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
pApp->Run();
CMyDoc* pMyDoc = new CMyDoc;
CMyView* pMyView = new CMyView;
CFrameWnd* pMyFrame = (CFrameWnd*)pApp->m_pMainWnd;
pMyFrame->m_pViewActive = pMyView;
pMyView->m_pDocument = pMyDoc;
// test Message Routing
cout << endl << "pMyFrame receive WM_CREATE, ";
cout << "routing path and call stack:" << endl;
AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);
cout << endl << "pMyView receive WM_PAINT, ";
cout << "routing path and call stack:" << endl;
AfxWndProc(0, WM_PAINT, 0, 0, pMyView);
cout << endl << "pMyView receive WM_COMMAND, ";
cout << "routing path and call stack:" << endl;
AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);
cout << endl << "pMyFrame receive WM_COMMAND, ";
cout << "routing path and call stack:" << endl;
AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);
}
//afxmsg_.h
enum AfxSig
{
AfxSig_end = 0, // [marks end of message map]
AfxSig_vv,
};
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },