在MFC中消息响应函数在程序中有三个相关的地方,一是头文件中的消息函数的声明;二是cpp文件中消息映射宏;三是cpp文件中的消息函数的函数体。
1 现在拿view文件来讲,h文件中包含以下信息:
protected:
afx_msg voidOnFilePrintPreview();
afx_msg voidOnRButtonUp(UINT nFlags, CPoint point);
afx_msg voidOnContextMenu(CWnd* pWnd, CPoint point);
DECLARE_MESSAGE_MAP()
public:
afx_msg intOnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg voidOnFileNew();
cpp中包含:
BEGIN_MESSAGE_MAP(Cmfcstd11View,CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT,&CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,&CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,&Cmfcstd11View::OnFilePrintPreview)
ON_WM_CONTEXTMENU()
ON_WM_RBUTTONUP()
ON_WM_CREATE()
ON_COMMAND(ID_FILE_NEW,&Cmfcstd11View::OnFileNew)
END_MESSAGE_MAP()
以及函数实现:
//这里仅仅列举两个函数的实现,还有很多
int Cmfcstd11View::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CView::OnCreate(lpCreateStruct) == -1)
return-1;
// TODO: 在此添加你专用的创建代码
return0;
}
void Cmfcstd11View::OnFileNew()
{
// TODO: 在此添加你专用的命令处理程序代码
::MessageBox(NULL,TEXT("OK"),TEXT("THis"),MB_OK);
}
2 现在把宏展开
#define DECLARE_MESSAGE_MAP() \
protected: \
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
virtualconst AFX_MSGMAP* GetMessageMap() const; \
把宏中行连接符去掉,可以看到:
protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtualconst AFX_MSGMAP* GetMessageMap() const;
其实就是在类中定义了两个函数,至于函数什么意义,我们先不管,接着往下看
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
constAFX_MSGMAP* theClass::GetMessageMap() const \
{ returnGetThisMessageMap(); } \
constAFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedeftheClass ThisClass; \
typedefbaseClass TheBaseClass; \
staticconst AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id,(WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG>(memberFxn) },
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end,(AFX_PMSG)0 } \
}; \
staticconst AFX_MSGMAP messageMap = \
{&TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return&messageMap; \
} \
PTM_WARNING_RESTORE
这个是BEGIN_MESSAGE_MAP和END_MESSAGE_MAP的宏定义,同样,我们把宏展开,
__pragma(warning(push )) __pragma(warning( disable : 4867))
const AFX_MSGMAP*theClass::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCALtheClass::GetThisMessageMap()
{
typedef theClass ThisClass;
typedef baseClassTheBaseClass;
static const AFX_MSGMAP_ENTRY_messageEntries[] =
{
{ WM_COMMAND,CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, static_cast<AFX_PMSG>(memberFxn) },
{0, 0, 0, 0,AfxSig_end, (AFX_PMSG)0 }
};
static const AFX_MSGMAPmessageMap =
{&TheBaseClass::GetThisMessageMap, &_messageEntries[0] };
return &messageMap;
}
__pragma(warning(pop ))
structAFX_MSGMAP_ENTRY; // declared belowafter CWnd
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL*pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
structAFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windowsmessages)
UINT nLastID; // used for entries specifying a range ofcontrol id's
UINT_PTR nSig; // signature type (action) or pointer tomessage #
AFX_PMSG pfn; // routine to call (or special value)
};
typedef void (AFX_MSG_CALLCCmdTarget::*AFX_PMSG)(void);
从上面的宏展开可以看到,每个类都声明了两个和消息映射有关的函数:
protected:
staticconst AFX_MSGMAP* PASCAL GetThisMessageMap();
virtualconst AFX_MSGMAP* GetMessageMap() const;
其中在静态函数GetThisMessageMap()中定义了一个针对类自身的静态消息数组:
static const AFX_MSGMAP_ENTRY _messageEntries[]
同时GetThisMessageMap()函数返回了一个结构体的指针:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL*pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
从这个结构中可以看出它既包含了父类的消息数据,又包含了自身的消息数组,因此通过函数GetThisMessageMap()能够得到类自身以及父类的消息响应函数。
从以上可以看出所谓的消息和消息函数的静态对照表就是上面所讲的GetThisMessage()函数中的static const AFX_MSGMAP_ENTRY_messageEntries[] 静态数组。
引孙鑫:MFC在后台维护一个窗口句柄与对应的C++对象指针的对照表,窗口句柄和对象指针是一一对应关系,当收到某一消息时,消息的第一个参数就指明该消息跟哪个窗口句柄相关,通过对照表,就可以找到与之相关的指针。然后把这个指针传递给应用程序框架窗口类的基类,后者会调用一个名为WindowProc的函数。这个函数定义位于WindCore.cpp文件。
LRESULTCWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsgdoes most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if(!OnWndMsg(message, wParam, lParam, &lResult))
lResult = DefWindowProc(message,wParam, lParam);
returnlResult;
}
OnWndMsg处理过程是:
首先判断消息是否有消息响应函数。
判断方法是在相应的窗口类中查找所需的消息响应函数。因为传递给WindowProc的是窗口子类指针,所以OnWndMsg会到相应的子类头文件中查找,看看是否有消息响应函数声明;再到子类的源文件中,看看BEGIN_MESSAGE_MAP和END_MESSAGE_MAP,这两个宏之间是否有相应的消息映射宏。
如果找到了消息响应函数就会调用该函数,否则就交给基类处理。