在这个传递网之中,如果消息需要从子类流向父类,那么是再简单不过的事了,可是在MFC中用来消息传递的C++类并不是单线传递的,作为Application Framework的重要结构之一 Document/View框架也具有处理消息的能力,(可能你还不了解Document/View框架,不过一点关系也没有),反正在MFC的设计中,消息应当也是有横向流动的机会的。
MFC中对于消息的规定如下:
如果是一般的Windows消息(WM_xxx),则只会按单线传递,由派生类流向基类。。。。
而如果消息是 WM_COMMAMD,那就有特殊的路线了,如下图:
命令消息的接收者 | 处理次序 |
FrameWnd | 1.View 2.FrameWnd 3.CWinApp |
View | 1.View 2.Document |
Document | 1.Document 2.Document template |
现在来仿真这整个流程,所有新增的一些成员函数如下:
类 | 与消息循环有关的成员函数 | 特征 |
none | AfxWndProc | global |
none | AfxCallWndProc | global |
CCmdTarget | OnCmdMsg | virtual |
CDocument | OnCmdMsg | virtual |
CWnd | WindowProc | virtual |
OnCommand | virtual | |
DefWindowProc | virtual | |
CFrameWnd | OnCommand | virtual |
OnCmdMsg | virtual | |
CView | OnCmdMsg | virtual |
其中全局函数AfxWndProc即是整个消息传递的起点,没调用一次该函数,便会推送一条消息,该函数原本在CWinThread::Run中被调用, 为了仿真的便利,这里将会直接在主函数main中调用,该函数原来有4个参数,为了使用的方便,这里再多加一个参数,以表示该消息的获得者。(推测消息驱动的起点就在CWinThread这里)
仿真的消息传递的过程如下:
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
CWnd* pWnd)
{
return AfxCallWndProc(pWnd,hWnd ,nMsg,wParam , lParam);
}
LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = pWnd->WindowProc(nMsg , wParam , lParam);
return lResult;
}
这里LRESULT是一个长整形的整数。
由于CMyFrameWnd和CMyView都没有重写WindowProc这个方法,所以到头来调用的还是CWnd的WindowProc,虽然看起来相似,但其实具体执行的效果都是不相同的,具体往下面看:
LRESULT CWnd::WindowProc(UINT nMsg , WPARAM wParam , LPARAM lParam)
{
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
if (nMsg == WM_COMMAND)
{
if (OnCommand(wParam, lParam))
{
return 1L;
}
else
return (LRESULT)DefWindowProc(nMsg , wParam , lParam);
}
pMessageMap = GetMessageMap();
for (; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
return 0;
}
在这里如果消息是WM_COMMAND,则CWnd::WindowProc调用OnCommand,这里的OnCommannd是一个虚方法,假如指向CMyFrameWnd,由于CMyFrameWnd重写了该方法,所以调用的是CMyFrameWnd::OnCommand,而如果指向CMyView,由于CMyView没有重写,所以调用的是CWnd::OnCommand ,这样调用的效果就完全不同了,具体实现如下:
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
return OnCmdMsg(0,0);
}
BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
return CWnd::OnCommand(wParam, lParam);
}
除了这里的一点点小小的不同之外,还有我们需要考虑的,之后的OnCmdMsg也是完全不同的,别忘了CFrameWnd和CView都重写了OnCmdMsg,而CWnd本身却没有实现(实现了也没用),所以,这个调用还是很绕的,调用进去了之后
假如消息指向CFrameWnd为:
那么接下来的反应就是:
BOOL CFrameWnd ::OnCmdMsg(UINT nID ,int nCode)
{
CView* pView = GetActiveView();
if (pView->OnCmdMsg(nID, nCode))
{
return TRUE;
}
if (CWnd::OnCmdMsg(nID, nCode))
{
return TRUE;
}
CWinApp* pApp = AfxGetApp();
if (pApp->OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
接下来,就会调用CView的OnCmdMsg , 在此之后,CView的OnCmdMsg实现中:
BOOL CView::OnCmdMsg(UINT nID, int nCode)
{
if (CWnd::OnCmdMsg(nID, nCode))
return TRUE;
BOOL bHandled = FALSE;
bHandled = m_pDocument->OnCmdMsg(nID,nCode);
return bHandled;
}
再去调用该CView的CWnd的OnCmdMsg,之后再去调用CDocuent的OnCmdMsg,在CDocument中
BOOL CDocument::OnCmdMsg(UINT nID, int nCode)
{
if (CCmdTarget::OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
在这里又调用了CCmdTarget::OnCmdMsg ,仿真如下:
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode)
{
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
for (pMessageMap = GetMessageMap();
pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
return FALSE;
}
这就是尽头了么?NO!,别忘了GetMessageMap也是虚函数,其实现被隐藏在宏之中,算了,不想写了,后面的就自己推一遍吧!
我这里直接给出整个消息传递的图出来~~~
先说一句,以上代码纯属仿真,所以去掉了一些多余的部分,所以看起来可能有一些莫名其妙,不用纠结,你只要知道这就是MFC的消息传递的顺序就可以了
这一张可能更清楚一点~~~
---摘自《深入浅出MFC》
完整的样例代码如下:
//MFC.h
#pragma once
#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
#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>
using namespace std;
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} \
};
#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()
};
typedef void(CCmdTarget::*AFX_PMSG)(void);
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT nSig;
AFX_PMSG pfn;
};
class CWinThread :public CCmdTarget
{
public:
CWinThread::CWinThread(){}
CWinThread::~CWinThread() {}
virtual BOOL InitInstance() {
cout << "CWinThread::InitInstance" << endl;
return TRUE;
}
virtual int Run() {
cout << "CWinThread::Run" << endl;
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" << endl;
return TRUE;
}
virtual BOOL InitInstance() {
cout << "CWinApp::InitInstance" << endl;
return TRUE;
}
virtual int Run() {
cout << "CWinApp::Run" << endl;
return CWinThread::Run();
}
DECLARE_MESSAGE_MAP()
};
typedef void(CWnd::*AFX_PMSGW)(void);
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;
public:
CFrameWnd::CFrameWnd(){}
CFrameWnd::~CFrameWnd(){}
BOOL Create();
CView* GetActiveView() const;
virtual BOOL PreCreateWindow();
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;
};
CWinApp* AfxGetApp();
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
CWnd* pWnd);
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)
{
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
for (pMessageMap = GetMessageMap();
pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
return FALSE;
}
BOOL CWnd::Create()
{
cout << "CWnd::Create" << endl;
return TRUE;
}
BOOL CWnd::CreateEx()
{
cout << "CWnd::CreateEx" << endl;
PreCreateWindow();
return TRUE;
}
BOOL CWnd::PreCreateWindow()
{
cout << "CWnd::PreCreateWindow" << endl;
return TRUE;
}
LRESULT CWnd::WindowProc(UINT nMsg , WPARAM wParam , LPARAM lParam)
{
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
if (nMsg == WM_COMMAND)
{
if (OnCommand(wParam, lParam))
{
return 1L;
}
else
return (LRESULT)DefWindowProc(nMsg , wParam , lParam);
}
pMessageMap = GetMessageMap();
for (; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
return 0;
}
LRESULT CWnd::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
return TRUE;
}
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
return OnCmdMsg(0,0);
}
BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
return CWnd::OnCommand(wParam, lParam);
}
BOOL CFrameWnd::Create()
{
cout << "CFrameWnd::Create" << endl;
CreateEx();
return TRUE;
}
BOOL CFrameWnd::PreCreateWindow()
{
cout << "CFrameWnd::PreCreateWindow" << endl;
return TRUE;
}
CView* CFrameWnd::GetActiveView() const
{
return m_pViewActive;
}
BOOL CFrameWnd ::OnCmdMsg(UINT nID ,int nCode)
{
CView* pView = GetActiveView();
if (pView->OnCmdMsg(nID, nCode))
{
return TRUE;
}
if (CWnd::OnCmdMsg(nID, nCode))
{
return TRUE;
}
CWinApp* pApp = AfxGetApp();
if (pApp->OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
BOOL CDocument::OnCmdMsg(UINT nID, int nCode)
{
if (CCmdTarget::OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
BOOL CView::OnCmdMsg(UINT nID, int nCode)
{
if (CWnd::OnCmdMsg(nID, nCode))
return TRUE;
BOOL bHandled = FALSE;
bHandled = m_pDocument->OnCmdMsg(nID,nCode);
return bHandled;
}
AFX_MSGMAP* CCmdTarget::GetMessageMap() const
{
return &CCmdTarget::messageMap;
}
AFX_MSGMAP CCmdTarget::messageMap = {
NULL,
&CCmdTarget::_messageEntries[0]
};
AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] =
{
{0,0,CCmdTargetid , 0, AfxSig_end ,0 }
};
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(CWinAppid ,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)
{
return AfxCallWndProc(pWnd,hWnd ,nMsg,wParam , lParam);
}
LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = pWnd->WindowProc(nMsg , wParam , lParam);
return lResult;
}
//MY.h
#pragma once
#include"MFC.h"
#include<iostream>
using namespace std;
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;
BOOL CMyWinApp::InitInstance()
{
cout << "CMyWinApp::InitInstance" << endl;
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;
}
}
}
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;
cout << endl << "pMyFrame received a WM_CREATE, routing path:" << endl;
AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);
cout << endl << "pMyView received a WM_PAINT, routing path:" << endl;
AfxWndProc(0, WM_PAINT, 0, 0, pMyView);
cout << endl << "pView received a WM_COMMAND, routing path:" << endl;
AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);
cout << endl << "pMyFrame received a WM_COMMAND, routing path:" << endl;
AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);
system("pause");
}