MFC消息映射与消息传递内幕.docx
《MFC消息映射与消息传递内幕.docx》由会员分享,可在线阅读,更多相关《MFC消息映射与消息传递内幕.docx(19页珍藏版)》请在冰点文库上搜索。
![MFC消息映射与消息传递内幕.docx](https://file1.bingdoc.com/fileroot1/2023-5/6/4247b9bc-5fdb-403a-8ce1-55665526a63e/4247b9bc-5fdb-403a-8ce1-55665526a63e1.gif)
MFC消息映射与消息传递内幕
MFC消息映射与消息传递内幕
////////////////////////////////////////////////////////////////////////////////////
/*********文章系列:
MFC技术内幕系列***********/
/************MFC技术内幕系列之(四)***********/
/*****文章题目:
MFC消息映射与消息传递内幕******/
/* Copyright(c)2002bigwhite */
/* AllrightsReserved */
/ *********关键字:
消息映射,消息传递************/
/* 时间:
2002.7.23 */
/* 注释:
本文所涉及的程序源代码均在Microsoft */
/ VisualStudio.NetEnterpriseArchitectEdition /
/* 开发工具包提供的源代码中 */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
引言:
Windows操作系统是以消息为基础,事件驱动的。
作为程序员了解操作系统的消息传递机制是非常必要的。
Microsoft的MFC又它自己的一套支持Windows操作系统消息机制的技术--消息映射(MessageMapping)和命令传递(CommandRouting),在这篇文章中我就详细的挖掘一下MFC的消息映射技术以及命令传递技术。
正文:
///////////////////////////////////////////////
/* 1.Windows消息概览 */
//////////////////////////////////////////////
对于消息,程序员应该不陌生。
WM_CREATE,WM_PAINT等等都是Windows程序设计中必不可缺少的组成部分。
大多有关MFCWin32编程的书籍都将Windows消息分为三大类即:
*标准消息:
任何以WM_开头的消息(WM_COMMAND除外);如:
WM_QUIT,WM_CREATE;
*命令消息:
WM_COMMAND;
*子窗口通知:
由子窗口(大多为控件)产生并发送到该控件所属的父窗口的消息。
(注意:
此类消息也 以WM_COMMAND形式出现)
消息类型我们已经了解了,下面我们就来看看消息映射是如何工作的:
//////////////////////////////////////////////////////
/* 2.MFC消息映射网的组成元素*/
//////////////////////////////////////////////////////
我的前几篇文章中涉及到了MFC内部建立的一些“网”技术,比如“执行期类型识别网”等,这回我们将建立一个消息映射网,这个网的建立与前面相同的是它也利用了一些神秘的宏。
下面我们就来掀开它们的神秘面纱。
我们先简单地看看这些宏在程序源文件中的什么地方?
//inxx.h
classtheClass
{
...//
DECLARE_MESSAGE_MAP()
};
//inxx.cpp
BEGIN_MESSAGE_MAP(theClass,baseClass)
ON_COMMAND(ID_MYCMD,OnMyCommand)
ON_WM_CREATE()
END_MESSAGE_MAP()
...//
这些宏的定义如下:
//inAfxwin.h
#defineDECLARE_MESSAGE_MAP()\
private:
\
staticconstAFX_MSGMAP_ENTRY_messageEntries[];\
protected:
\
staticconstAFX_MSGMAPmessageMap;\
staticconstAFX_MSGMAP*PASCALGetThisMessageMap();\
virtualconstAFX_MSGMAP*GetMessageMap()const;\
#defineBEGIN_MESSAGE_MAP(theClass,baseClass)\
constAFX_MSGMAP*PASCALtheClass:
:
GetThisMessageMap()\
{return&theClass:
:
messageMap;}\
constAFX_MSGMAP*theClass:
:
GetMessageMap()const\
{return&theClass:
:
messageMap;}\
AFX_COMDATconstAFX_MSGMAPtheClass:
:
messageMap=\
{&baseClass:
:
GetThisMessageMap,&theClass:
:
_messageEntries[0]};\
AFX_COMDATconstAFX_MSGMAP_ENTRYtheClass:
:
_messageEntries[]=\
{\
#defineEND_MESSAGE_MAP()\
{0,0,0,0,AfxSig_end,(AFX_PMSG)0}\
};\
DECLARE_MESSAGE_MAP()宏为每个类添加了四个东东,包括那个重要的消息映射表messageMap和消息入口结构数组AFX_MSGMAP_ENTRY_messageEntries[];BEGIN_MESSAGE_MAP(theClass,baseClass)和END_MESSAGE_MAP()宏则初始化了它们,随后我将带领大家看看这个初始化过程。
///////////////////////////////////////////////
/* 3.MFC消息映射表 */
//////////////////////////////////////////////
下面我们看看消息映射表messageMap和消息入口结构AFX_MSGMAP_ENTRY的定义:
//inAfxwin.h
structAFX_MSGMAP_ENTRY
{
UINTnMessage; //windowsmessage
UINTnCode; //controlcodeorWM_NOTIFYcode
UINTnID; //controlID(or0forwindowsmessages)
UINTnLastID; //usedforentriesspecifyingarangeofcontrolid's
UINT_PTRnSig; //signaturetype(action)orpointertomessage#
AFX_PMSGpfn; //routinetocall(orspecialvalue)
};
structAFX_MSGMAP
{
#ifdef_AFXDLL
constAFX_MSGMAP*(PASCAL*pfnGetBaseMap)();//基类的映射表指针,本程序将使用
#else
constAFX_MSGMAP*pBaseMap;
#endif
constAFX_MSGMAP_ENTRY*lpEntries;
};
其中AFX_MSGMAP结构中包含一个基类的映射表指针和一个指向消息入口结构AFX_MSGMAP_ENTRY的指针。
/////////////////////////////////////////////////
/* 4.MFC消息映射宏展开 */
/////////////////////////////////////////////////
上面的宏展开后代码如下:
(以CMaimFrame为例)
//inMaimFrm.h
classCMaimFrame:
publicCFrameWnd
{
...//
private:
staticconstAFX_MSGMAP_ENTRY_messageEntries[];
protected:
staticconstAFX_MSGMAPmessageMap;
staticconstAFX_MSGMAP*PASCALGetThisMessageMap();
virtualconstAFX_MSGMAP*GetMessageMap()const;
};
//inMaimFrm.cpp
constAFX_MSGMAP*PASCALCMaimFrame:
:
GetThisMessageMap()
{return&CMaimFrame:
:
messageMap;}
constAFX_MSGMAP*CMaimFrame:
:
GetMessageMap()const
{return&CMaimFrame:
:
messageMap;}
AFX_COMDATconstAFX_MSGMAPtheClass:
:
messageMap=
{&CFrameWnd:
:
GetThisMessageMap,&CMaimFrame:
:
_messageEntries[0]};
AFX_COMDATconstAFX_MSGMAP_ENTRYCMaimFrame:
:
_messageEntries[]=
{
{ ...// }
...
{0,0,0,0,AfxSig_end,(AFX_PMSG)0}
};
相信大家看了后大多源代码都能够理解,但是AFX_MSGMAP_ENTRY结构还是能够引起我们的兴趣的。
下面让我们看看_messageEntries[]是如何被初始化的:
我们还是举例来说明吧!
(还是CMainFrame为例吧)
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_MYCMD,OnMyCommand)
END_MESSAGE_MAP()
大家看到了夹在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()之间的宏,这些宏可分为基类,一类是Windows预定义消息宏(比如:
ON_WM_CREATE(),ON_WM_DESTROY()等定义在afxmsg_.h中的MessagemaptablesforWindowsmessages),一类是自定义的ON_COMMAND宏以及类似的如ON_UPDATE_COMMAND_UI等宏。
//inafxmsg_.h
//MessagemaptablesforWindowsmessages
#defineON_WM_CREATE()\
{WM_CREATE,0,0,0,AfxSig_is,\
(AFX_PMSG)(AFX_PMSGW)\
(static_cast:
*)(LPCREATESTRUCT)>(OnCreate))},
#defineON_COMMAND(id,memberFxn)\
{WM_COMMAND,CN_COMMAND,(WORD)id,(WORD)id,AfxSigCmd_v,\
static_cast(memberFxn)},
AFX_MSGMAP_ENTRY结构初始化过程:
AFX_COMDATconstAFX_MSGMAP_ENTRYCMaimFrame:
:
_messageEntries[]=
{
{WM_CREATE,0,0,0,AfxSig_is,
(AFX_PMSG)(AFX_PMSGW)
(static_cast:
*)(LPCREATESTRUCT)>(OnCreate))},
{WM_COMMAND,CN_COMMAND,(WORD)ID_MYCMD,(WORD)ID_MYCMD,AfxSigCmd_v,\
static_cast(OnMyCommand)},
{0,0,0,0,AfxSig_end,(AFX_PMSG)0}
};
现在一切都清楚了吧!
//////////////////////////////////////////////////
/* 5.MFC消息映射网的连接 */
//////////////////////////////////////////////////
MFC消息映射网的连接也是在初始化过程中完成的,其建立过程很简单。
主要有关成员有:
private:
staticconstAFX_MSGMAP_ENTRY_messageEntries[];
protected:
staticconstAFX_MSGMAPmessageMap;
和BEGIN_MESSAGE_MAP()宏开后的
AFX_COMDATconstAFX_MSGMAPtheClass:
:
messageMap=
{&baseClass:
:
GetThisMessageMap,&theClass:
:
_messageEntries[0]};
该宏将pfnGetBaseMap赋值为其基类的messageMap地址;将AFX_MSGMAP_ENTRY*lpEntries赋值为该类的_messageEntries[0];
这样一个类不仅拥有本类的messageMap,而且还拥有其基类的messageMap,依此类推MFC消息映射网的连接
就建立了,最终的基类都是CCmdTarget
//////////////////////////////////////////////////
/* 6.MFC命令传递机制概述 */
//////////////////////////////////////////////////
有了MFC消息映射网就为命令传递打下了坚实的基础。
Win32API程序员都熟悉传统的API编程都有一个WndProc回调函数来集中处理各种的Windows消息,然而在MFC中我们却怎么也看不见WndProc回调函数的踪影了。
而MFC命令传递机制恰是为了将各种消息“拐弯抹角”地送到各个对应的"WndProc"函数的一种技术。
MFC是如何将各种Windows消息准确的送到期该区的地方呢?
MFC使用了钩子函数等技术来保证其准确性和全面性。
不知大家是否还记得MFC在注册窗口类时作了什么?
BOOLAFXAPIAfxEndDeferRegisterClass(LONGfToRegister)//部分源代码
{
...//
//commoninitialization
WNDCLASSwndcls;
memset(&wndcls,0,sizeof(WNDCLASS)); //startwithNULLdefaults
wndcls.lpfnWndProc=DefWindowProc;
...//
}
可以看到MFC注册时将wndcls.lpfnWndProc赋值为DefWindowProc函数,那么实际上是否消息都是由它处理的呢?
显然不可能,MFC又不知道我们要处理什么消息。
那么还有什么函数能帮我们处理消息呢?
这就是我要讲的; 在MFC技术内幕系列之
(二)----《MFC文档视图结构内幕》中曾提到“CWnd:
:
CreateEx函数调用了AfxHookWindowCreate(this);后者是干什么的呢?
其实它与消息映射和命令传递有关。
”
实际上MFC命令传递机制就是从这里AfxHookWindowCreate(this)开始的。
还是老办法看看代码吧:
//inwincore.cpp
voidAFXAPIAfxHookWindowCreate(CWnd*pWnd)
{
...//
if(pThreadState->m_hHookOldCbtFilter==NULL)
{
pThreadState->m_hHookOldCbtFilter=:
:
SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook,NULL,:
:
GetCurrentThreadId());
if(pThreadState->m_hHookOldCbtFilter==NULL)
AfxThrowMemoryException();
}
...//
}
该函数设置了消息钩子,其钩子处理函数为_AfxCbtFilterHook;这里简介一下钩子函数:
用我的理解,钩子就是能给你一个在某个消息到达其默认的处理函数之前处理该消息机会的工具。
与钩子有关的函数主要有三个:
HHOOKSetWindowsHookEx(intidHook,HOOKPROClpfn,HINSTANCEhMod,DWORDdwThreadId);
LRESULTCallNextHookEx(HHOOKhhk, intnCode, WPARAMwParam, LPARAMlParam );
BOOLUnhookWindowsHookEx(HHOOKhhk //handletohookprocedure);
关于这三个函数我也不想多解释,大家看看MFC有关文档吧!
这里主要讲的是在AfxHookWindowCreate(CWnd*pWnd)中注册的钩子函数的类型(hooktype)--WH_CBT;
有关WH_CBT,MFC文档时如是说的:
Installsahookprocedurethatreceivesnotificationsusefultoacomputer-basedtraining(CBT)application.Thesystemcallsthisfunction(这里指的是_AfxCbtFilterHook)beforeactivating,creating,destroying,minimizing,maximizing,moving,orsizingawindow;beforecompletingasystemcommand;beforeremovingamouseorkeyboardeventfromthesystemmessagequeue;beforesettingthekeyboardfocus;orbeforesynchronizingwiththesystemmessagequeue.Acomputer-basedtraining(CBT)applicationusesthishookproceduretoreceiveusefulnotificationsfromthesystem.
/////////////////////////////////////////////
/* 7.偷换“窗口函数” */
/////////////////////////////////////////////
这会知道了吧,当发生窗口(包括子窗口)发生被激活,创建,撤