MFC消息传递.docx
《MFC消息传递.docx》由会员分享,可在线阅读,更多相关《MFC消息传递.docx(24页珍藏版)》请在冰豆网上搜索。
MFC消息传递
MFC消息映射与消息传递黑幕
引言:
Windows操作系统是以消息为基础,事件驱动的。
作为程序员了解操作系统的消息传递机制是超级必要的。
Microsoft的MFC又它自己的一套支持Windows操作系统消息机制的技术--消息映射(MessageMapping)和命令传递(CommandRouting),在这篇文章中我就详细的挖掘一下MFC的消息映射技术和命令传递技术。
消息概览
关于消息,程序员应该不陌生。
WM_CREATE,WM_PAINT等等都是Windows程序设计中必不可缺少的组成部份。
大多有关MFCWin32编程的书籍都将Windows消息分为三大类即:
*标准消息:
任何以WM_开头的消息(WM_COMMAND除外);如:
WM_QUIT,WM_CREATE;
*命令消息:
WM_COMMAND;
*子窗口通知:
由子窗口(大多为控件)产生并发送到该控件所属的父窗口的消息。
(注意:
此类消息也以WM_COMMAND形式显现)
消息映射网的组成元素
我的前几篇文章中涉及到了MFC内部成立的一些“网”技术,比如“执行期类型识别网”等,这回咱们将成立一个消息映射网,那个网的成立与前面相同的是它也利用了一些神秘的宏。
下面咱们就来掀开它们的神秘面纱。
咱们先简单地看看这些宏在程序源文件中的什么地址?
classtheClass
{
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(theClass,baseClass)
ON_COMMAND(ID_MYCMD,OnMyCommand)
ON_WM_CREATE()
END_MESSAGE_MAP()
...//
这些宏的概念如下:
#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()宏那么初始化了它们,随后我将率领大伙儿看看那个初始化进程。
消息映射表
下面咱们看看消息映射表messageMap和消息入口结构AFX_MSGMAP_ENTRY的概念:
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的指针。
消息映射宏展开
上面的宏展开后代码如下:
(以CMaimFrame为例)
classCMaimFrame:
publicCFrameWnd
{
...//
private:
staticconstAFX_MSGMAP_ENTRY_messageEntries[];
protected:
staticconstAFX_MSGMAPmessageMap;
staticconstAFX_MSGMAP*PASCALGetThisMessageMap();
virtualconstAFX_MSGMAP*GetMessageMap()const;
};
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()等概念在中的MessagemaptablesforWindowsmessages),一类是自概念的ON_COMMAND宏和类似的如ON_UPDATE_COMMAND_UI等宏。
//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}
};
此刻一切都清楚了吧!
消息映射网的连接
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
命令传递机制概述
有了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注册时将赋值为DefWindowProc函数,那么事实上是不是消息都是由它处置的呢?
显然不可能,MFC又不明白咱们要处置什么消息。
那么还有什么函数能帮咱们处置消息呢?
这确实是我要讲的;在MFC技术黑幕系列之
(二)----《MFC文档视图结构黑幕》中曾提到“CWnd:
:
CreateEx函数挪用了AfxHookWindowCreate(this);后者是干什么的呢?
其实它与消息映射和命令传递有关。
”
事实上MFC命令传递机制确实是从那个地址AfxHookWindowCreate(this)开始的。
仍是老方法看看代码吧:
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.偷换“窗口函数”
这会明白了吧,当发生窗口(包括子窗口)发生被激活,创建,撤销,最小化等时候,应用程序将挪用
_AfxCbtFilterHook函数;下面就让咱们看看_AfxCbtFilterHook函数做了个啥:
//Windowcreationhooks
LRESULTCALLBACK_AfxCbtFilterHook(intcode,WPARAMwParam,LPARAMlParam)
{
_AFX_THREAD_STATE*pThreadState=_afxThreadState.GetData();
if(code!
=HCBT_CREATEWND)
{
//waitforHCBT_CREATEWNDjustpassotherson...
returnCallNextHookEx(pThreadState->m_hHookOldCbtFilter,code,
wParam,lParam);
}
...//CWnd*pWndInit=pThreadState->m_pWndInit;
HWNDhWnd=(HWND)wParam;
WNDPROColdWndProc;
if(pWndInit!
=NULL)
{
#ifdef_AFXDLL
AFX_MANAGE_STATE(pWndInit->m_pModuleState);
#endif
//thewindowshouldnotbeinthepermanentmapatthistime
ASSERT(CWnd:
:
FromHandlePermanent(hWnd)==NULL);
//connecttheHWNDtopWndInit...
pWndInit->Attach(hWnd);
//allowothersubclassingtooccurfirst
pWndInit->PreSubclassWindow();//***
WNDPROC*pOldWndProc=pWndInit->GetSuperWndProcAddr();
ASSERT(pOldWndProc!
=NULL);
//subclassthewindowwithstandardAfxWndProc
WNDPROCafxWndProc=AfxGetAfxWndProc();//***
oldWndProc=(WNDPROC)SetWindowLongPtr(hWnd,GWLP_WNDPROC,
(DWORD_PTR)afxWndProc);//***
ASSERT(oldWndProc!
=NULL);
if(oldWndProc!
=afxWndProc)
*pOldWndProc=oldWndProc;
pThreadState->m_pWndInit=NULL;
}
...//
lCallNextHook:
LRESULTlResult=CallNextHookEx(pThreadState->m_hHookOldCbtFilter,code,
wParam,lParam);
#ifndef_AFXDLL
if(bContextIsDLL)
{
:
:
UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
pThreadState->m_hHookOldCbtFilter=NULL;
}
#endif
returnlResult;
}
voidCWnd:
:
PreSubclassWindow()
{
//nodefaultprocessing
}
//alwaysindirectlyaccessedviaAfxGetAfxWndProc
WNDPROCAFXAPIAfxGetAfxWndProc()
{
#ifdef_AFXDLL
returnAfxGetModuleState()->m_pfnAfxWndProc;
#else
return&AfxWndProc;
#endif
}
原先_AfxCbtFilterHook函数偷换了窗口函数,将原先的DefWndProc换成AfxWndProc函数.
的“WndProc”函数
AfxWndProc函数就能够够说是MFC的“WndProc”函数,它也是MFC中消息传递的开始,其代码如下:
//TheWndProcforallCWnd'sandderivedclasses
LRESULTCALLBACKAfxWndProc(HWNDhWnd,UINTnMsg,WPARAMwParam,LPARAMlParam)
{
//specialmessagewhichidentifiesthewindowasusingAfxWndProc
if(nMsg==WM_QUERYAFXWNDPROC)
return1;
//allothermessagesroutethroughmessagemap
CWnd*pWnd=CWnd:
:
FromHandlePermanent(hWnd);
ASSERT(pWnd!
=NULL);
ASSERT(pWnd->m_hWnd==hWnd);
returnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);
}
//OfficialwaytosendmessagetoaCWnd
LRESULTAFXAPIAfxCallWndProc(CWnd*pWnd,HWNDhWnd,UINTnMsg,
WPARAMwParam=0,LPARAMlParam=0)
{
_AFX_THREAD_STATE*pThreadState=_afxThreadState.GetData();
MSGoldState=pThreadState->m_lastSentMsg;//savefornesting
pThreadState->m_lastSentMsg.hwnd=hWnd;
pThreadState->m_lastSentMsg.message=nMsg;
pThreadState->m_lastSentMsg.wParam=wParam;
pThreadState->m_lastSentMs