假如代码生成一个菜单项。如下:
cMenu.AppendMenu(MF_POPUP,300,_T("文件"));
怎样让这个菜单项响应事件呢?
首先在.h中声明:
afx_msg void Hi();//声明一个消息处理函数Hi();
然后消息映射表里添加映射:
BEGIN_MESSAGE_MAP(CUseControlDlg, CDialog)
ON_COMMAND(300, &CUseControlDlg::Hi)//第一个参数是控件的ID
END_MESSAGE_MAP()
最后是函数体:
void CUseControlDlg::Hi()
{
// TODO: 在此添加命令处理程序代码
AfxMessageBox(_T("hi"));
}
这里一个知识点:
Resource.h打开,可以看到你即使给控件使用了字母ID
也是对应到一个宏。对应数字。
#define IDC_BUTTON_OK 1001
视类增加一个消息相应函数后,在源文件中都会增加三处代码。
1 消息相应函数原型。
在CDrawView中会有这样的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
protected :
// {{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
// }}AFX_MSG
DECLARE_MESSAGE_MAP()
在DECLARE_MESSAGE_MAP()宏上有两个AFX_MSG注释宏,在这两个注释宏之间有一个函数原型OnLButtonDown,因为她位于两个注释宏之间,所以是以灰色显示,还函数声明的前部有一个afx_msg限定符,这也是一个宏。该宏表明这个函数是一个消息响应函数的声明。
AFX_MSG(CLASSNAME):
ClassWizard and AppWizard insert specially formatted comment delimiters in your source code files to mark the places in your files that ClassWizard can write to. AFX_MSG is used to mark the beginning and end of ClassWizard entries in your header file (.H) related to message maps
2 ON_WM_LBUTTONDOWN()消息映射宏
在CDrawView类的源文件中,有如下代码:
// {{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_LBUTTONDOWN()
// }}AFX_MSG_MAP
END_MESSAGE_MAP()
在 上述代码中,BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP() 这两个宏之间定义了CDrawView类的消息映射表,其中有一个ON_WM_LBUTTONDOWN()这消息映射宏,这个宏的作用就是吧鼠标左键按下消息WM_LBUTTONDOWN与一个消息相应函数关联起来。通过这种机制,一旦有消息产生,程序就会调用相应的消息相应函数来进行处理。
3 消息响应函数的定义
在CDrawView的源文件中,可以看到OnLButtonDown函数的定义。
MFC消息映射机制的具体实现方法是:
在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息和对应的消息处理指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理该消息,如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。
MFC消息映射的具体实现过程。MFC在后台维护了一个窗口句柄和对应的C++对象的对照表,以一个实例CDrawView类为例,与它的对象相关的有一个窗口,窗口当然有它的窗口句柄,该句柄与CDrawView对象的一个指针存在这意义对应关系,在窗口句柄与C++对招标中就维护了这种对应关系。当收到某一消息时,消息的第一个参数就指明该消息与那个窗口句柄相关,通过对照表,就可以找到与之相关的C++对象指针。然后把这个指针传递给应用程序框架窗口类的基类,后者会调用一个名为WindowProc的函数。 其具体代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0 ;
if ( ! OnWndMsg(message, wParam, lParam, & lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
根据WindowProc函数的定义,我们发现它是一个虚函数。同时,也发现,CWnd::WindowProc函数内部调用了一个OnWndMsg的函数,真正的消息路由,也就是消息映射就是由此函数完成的。CWnd::OnWndMsgvirtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult );
Return Value
Nonzero if message was handled; otherwise 0.
Parameters
message
Specifies the message to be sent.
wParam
Specifies additional message-dependent information.
lParam
Specifies additional message-dependent information.
pResult
The return value of WindowProc. Depends on the message; may be NULL.
//CWnd::WindowProc
virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam );//
nWndMsg部分代码如下:BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
LRESULT lResult = 0;
// special case for commands
if (message == WM_COMMAND)
{
if (OnCommand(wParam, lParam))
{
lResult = 1;
goto LReturnTrue;
}
return FALSE;
}
// special case for notifies
if (message == WM_NOTIFY)
{
NMHDR* pNMHDR = (NMHDR*)lParam;
if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
goto LReturnTrue;
return FALSE;
}
OnWndMsg函数的处理过程是:
判断消息是否有消息响应函数。
判断的方法是在相应窗口类中查找所需的消息响应函数。因为传递给WindowProc函数的是窗口子类指针,所以OnWndMsg会到相应的子类头文件中查找,看看DECLARE_MESSAGE_MAP()宏之上,两个AFX_MSG注释宏之间是否有相应的消息响应函数原型的声明;再到子类的源文件中,看看BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()之间是否有相应的消息映射宏。
如果通过上述步骤,找到了消息相应函数,那么接着就会调用改响应函数,对消息进行处理。如果在子类中没有找到消息响应函数,那么就交由基类进行处理。
通过以上步骤,MFC就实现了具体的消息映射,从而完成了对消息的相应。