在MFC原代码中有如下代码:AFX_STATIC BOOL AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,
AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo)
// return TRUE to stop routing
{
ASSERT_VALID(pTarget);
UNUSED(nCode); // unused in release builds union MessageMapFunctions mmf;
mmf.pfn = pfn;
BOOL bResult = TRUE; // default is ok if (pHandlerInfo != NULL)
{
// just fill in the information, don't do it
pHandlerInfo->pTarget = pTarget;
pHandlerInfo->pmf = mmf.pfn;
return TRUE;
} switch (nSig)
{
case AfxSig_vv:
// normal command or control notification
ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED
ASSERT(pExtra == NULL);
(pTarget->*mmf.pfn_COMMAND)(); // ******注意这行*****
break;
为什么执行到*****这行 之后 程序跳到 CWinApp::OnFileNew()了呢? (pTarget->*mmf.pfn_COMMAND)()跟 CWinApp::OnFileNew() 有什么关系呢?请高手指点。
AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo)
// return TRUE to stop routing
{
ASSERT_VALID(pTarget);
UNUSED(nCode); // unused in release builds union MessageMapFunctions mmf;
mmf.pfn = pfn;
BOOL bResult = TRUE; // default is ok if (pHandlerInfo != NULL)
{
// just fill in the information, don't do it
pHandlerInfo->pTarget = pTarget;
pHandlerInfo->pmf = mmf.pfn;
return TRUE;
} switch (nSig)
{
case AfxSig_vv:
// normal command or control notification
ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED
ASSERT(pExtra == NULL);
(pTarget->*mmf.pfn_COMMAND)(); // ******注意这行*****
break;
为什么执行到*****这行 之后 程序跳到 CWinApp::OnFileNew()了呢? (pTarget->*mmf.pfn_COMMAND)()跟 CWinApp::OnFileNew() 有什么关系呢?请高手指点。
解决方案 »
- 分享一下我的blog【集成VS2008命令行编译到.sln文件的右键菜单】
- 学习网络原理遇到的几个疑惑
- 恳请高人帮助:如何用VC++将word文档中字符串内容转成CString?
- 根据键盘键值取得键名的问题!
- bohut请各位网友给我儿子(女儿)取个名字!呵呵
- ************高分再求:谁有数据传输相关文档和代码*****************
- Port重定向问题...
- 从数据库中读image类型的字段后,如何将它存成.bmp文件?
- 如何在dll中存储数据?
- 如何使数据库(Oracle)登录窗口不再弹出?
- 如何自己写一个“窗口”菜单。。。
- 求教,简单问题:如何根据句柄得到CButton控件的实例或实例指针?
关系不大
return _AfxDispatchCmdMsg(this, nID, nCode,
lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
来调用消息处理函数
lpEntry->pfn就是类中用宏关联的处理函数指针.
在AfxDispatchCmdMsg里面 mmf.pfn = pfn;
这一句就是将函数OnFileNew的地址赋值为mmf.pfn由于mmf是个联合体,因此pfn 和pfn_COMMAND的值是一样的
通过下面的语句来调用OnFileNew()
(pTarget->*mmf.pfn_COMMAND)();
至于为什么要这样调用,主要市考虑函数的堆栈的处理要和OnFileNew一致
pTarget指向App对象
mmf是个联合union,它的成员mmf.pfn_COMMAND()是个指针,指向实际的WM_COMMAND的处理函数,处理函数是CcmdTarget或其派生类的成员函数,因此必须要用这种稍显怪异的方法进行函数指针所指函数的调用.MFC中WM_COMMAND消息的处理过程(以SDI为例):
消息首先到发送消息的窗口的OnCommand,这是虚函数,所以直接调用发送消息的窗口所属类的实现,这里是CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam);[WinFrm.cpp] 这个函数中,如果消息是在线帮助请求,则发送一个WM_COMMANDHELP消息,并以ID_DEFAULT_HELP为wParam参数发送WM_COMMAND消息,同时返回;否则调用CWnd的实现CWnd::OnCommand()[WinCore.cpp].
CWnd::OnCommand()中,首先检查是不是控件发出的消息if (hWndCtrl == NULL),如果是,它使用ReflectLastMsg(hWndCtrl)将消息直接发给控件并返回.否则,确认消息是菜单或加速键,首先检查对应的界面元素是否被禁用,没有的话将消息传给虚函数OnCmdMsg(),这里将调用CFrame::OnCmdMsg[WinFrm.cpp].
Cframe::OnCmdMsg将消息按下面路由进行传送:活动视图,主窗口,应用程序.其中消息到活动视图中时,如用户的视图类没有重载此虚函数,则调用转到CView::OnCmdMsg()[ViewCore.cpp].在这里,它将消息转它的基类处理(CcmdTarget::OnCmdMsg[CmdTarg.cpp]),如基类不处理,将会转交它所属文档类去处理,文档类不处理时,后继路由上的主窗口或应用程序才有机会处理.
CcmdTarget::OnCmdMsg遍历消息映射表查找消息处理函数,找不到返回FALSE进行后继处理,找到则调用指定义的处理函数:_ AfxDispatchCmdMsg.该函数前的static仅表示该全局函数在该文件内可见,其它文件不得调用.其参数nSig是个函数签名,pfn时处理函数的指针,二者都来自消息映射表入口,它将根据签名的不同执行不同的操作(因为它们的对应处理函数的返回值或参数有所区别,需要进行参数生成并将pfn转换成合适类型的函数指针进行的调用),如一般菜单命令的签名是AfxSig_xx.
注意到_ AfxDispatchCmdMsg开头的那句 union MessageMapFunctions mmf; mmf.pfn = pfn;没有?这个联合用于将pfn根据签名的不同转换成不同的类型:
//AfxImpl.cpp
union MessageMapFunctions
{
AFX_PMSG pfn; // generic member function pointer void (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND)();
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_bCOMMAND)();
void (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND_RANGE)(UINT);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND_EX)(UINT);
……//后面列出了所有可能的消息处理函数的原型,详见源码
} ;
void (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND)();代表一个指向CcmdTarget类的成员函数的指针,返回值及参数都为空,其它原型同理.因为这是个联合,不管用哪个成员来进行函数调用,实际都是用pfn这个指针----它已经在上面初使化为消息表入口中提供的特定处理函数的指针了.注意它是指向CCmdTarget类的成员函数的指针,按C++函数指针的规则,使用语法如:pTarget->*mmf.pfn_COMMAND()//这里pTarget须是个CcmdTarget或其派生类的对象.
因此,有了函数签名后,对消息处理函数的调用就使用mmf中对应的函数原形以实现参数与返回值的匹配.
当上面列出的所有消息路由都没处理消息时,由CWnd::DefWindowProc()处理.
上面所有东西由一个称为函数签名的东西维系的.它定义于消息入口中:
//AfxWin.h
struct AFX_MSGMAP_ENTRY
{
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)
};
中间的nSig就是函数签名.
//AfxMsg_.h中定义了已知的函数签名,这是个enum:
enum AfxSig
{
AfxSig_end = 0, // [s end of message map] AfxSig_bD, // BOOL (CDC*)
AfxSig_bb, // BOOL (BOOL)
AfxSig_bWww, // BOOL (CWnd*, UINT, UINT)
……//未完,详见源码.
} ;
消息映射宏的实现其实就是定义了个静态的消息入口表,入口表是个AFX_MSGMAP_ENTRY一维数组,每个元素对应于一条消息映射宏.但不限于此,DECLARE_MESSAGE_MAP()还定义了基类消息入口表的指针以实现向基类传递消息.
AfxMsg_.h中的注释说明了消息入口表项的格式(共9种):
// Entries in a message map (a 'AFX_MSGMAP_ENTRY') table can be of 9 formats
//
// 1) control notification message (i.e. in response to WM_COMMAND)
// WM_COMMAND, nNotifyCode, nControlID, nControlID, signature type, parameterless member function
// (eg: WM_COMMAND, LBN_SELCHANGE, IDC_LISTBOX, AfxSig_vv, ... )
// 2) control notification message range (i.e. in response to WM_COMMAND)
// WM_COMMAND, nNotifyCode, nControlIDFirst, nControlIDLast, signature type, parameterless member function
// (eg: WM_COMMAND, LBN_SELCHANGE, IDC_LISTBOX1, IDC_LISTBOX5, AfxSig_vw, ... )
// 3) WM_NOTIFY notification
// WM_NOTIFY, nNotifyCode, nControlID, nControlID, signature type, ...)
// 3) Update Command UI
// -1, 0, nControlID, 0, signature Unknown, parameterless member function
// 4) Update Command UI Range
// -1, 0, nControlIDFirst, nControlIDLast, signature Unknown, parameterless member function
// 5) menu/accelerator notification message (i.e. special case of first format)
// WM_COMMAND, 0, nID, 0, signature type, parameterless member function
// (eg: WM_COMMAND, 0, IDM_FILESAVE, 0, AfxSig_vv, ... )
// 6) menu/accelerator notification message range
// WM_COMMAND, 0, nIDFirst, nIDLast, signature type, parameterless member function
// (eg: WM_COMMAND, 0, IDM_FILE_MRU1, IDM_FILE_MRU4, AfxSig_vw, ... )
// 7) constant windows message
// nMessage, 0, 0, 0, signature type, member function
// (eg: WM_PAINT, 0, ...)
// 8) variable windows message (using RegisterWindowMessage)
// 0xC000, 0, 0, 0, &nMessage, special member function
//
// The end of the message map is ed with a special value
// 0, 0, AfxSig_end, 0
/////////////////////////////////////////////////////////////////////////////