VC初学者入门系列之二:消息循环.doc_第1页
VC初学者入门系列之二:消息循环.doc_第2页
VC初学者入门系列之二:消息循环.doc_第3页
VC初学者入门系列之二:消息循环.doc_第4页
VC初学者入门系列之二:消息循环.doc_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

VC初学者入门系列之二:消息循环作者:jxhnuaa(铁凌)适用读者:VC初学者并有C+基础。VC初学者入门系列之一:窗口类的诞生一、传统SDK程序的消息循环 在传统的SDK程序中,消息循环是很简单的,也许你不信,那我们就看看下面这段代码吧: #include LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)static TCHAR szAppName = TEXT (HelloWin) ;WNDCLASwndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ;wndclass.lpszClassName= szAppName ;RegisterClass (&wndclass);hwnd = CreateWindow( szAppName,NULL); ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0) TranslateMessage (&msg) ; DispatchMessage (&msg) ; return msg.wParam ;LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) switch (message) case WM_CREATE: case WM_PAINT: case WM_DESTROY:PostQuitMessage (0) ;return 0 ; return DefWindowProc (hwnd, message, wParam, lParam) ;在WinMain 中 CreateWindow通过一个参数将创建的窗口和窗口类(见窗口类的诞生一文)联系起来,这样该窗口的所有消息都将发送到该窗口类的窗口函数WndProc,其后WndProc根据不同的消息给予不同的动作。 二、MFC期望的消息循环在传统的SDK程序中消息循环是非常简单的,并且将窗口和窗口函数绑定在一起。而在MFC中就出现了问题,比如CDocument类,不是窗口,所以没有窗口类,但是我也想让它响应消息,怎办?问题不仅仅如此,我们再看看MFC的消息,就会发现更多问题。 MFC将消息分为三大类:1.标准消息,即除WM_COMMAND之外的任何WM_开头的消息,任何派生自CWnd的类都可以接受该消息,并按照继承关系接受(如从CScrollView到CView再到CWnd)。2.命令消息,即WM_COMMAND,任何派生自CCmdTarget的类,兼可接受该消息,接受顺序如下图所示,其中标号标注了接受消息的顺序,箭头代表调用顺序 :图1 消息的拐弯流动 3.Control Notification,通知类消息,也以WM_COMMAND形式出现,由控件产生,通知其父窗口。 三、消息宏背后的秘密知道了MFC消息流动的要求,那MFC是怎样实现的呢?当一个消息出现时,Application FrameWork怎么知道将该消息发送给哪个对象的呢?其实都是CCmdTarget类在作怪,所有能够接受消息的类都必须继承于CCmdTarget类,因为这些类都一个共同的特征:含有DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP三个宏。啊!就这三个宏组织了一张庞大的消息映射网,也许你不信,那我们就看看这三个宏是怎样定义的: #define DECLARE_MESSAGE_MAP()private: static const AFX_MSGMAP_ENTRY _messageEntries;protected: static AFX_DATA const AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const;#define BEGIN_MESSAGE_MAP(theClass, baseClass) const AFX_MSGMAP* theClass:GetMessageMap() constreturn &theClass:messageMap;AFX_DATADEF const AFX_MSGMAP theClass:messageMap = &baseClass:messageMap, &theClass:_messageEntries0;const AFX_MSGMAP_ENTRY theClass:_messageEntries=#define END_MESSAGE_MAP()0,0,0,0,AfxSig_end,(AFX_pMSG)0;typedef void (AFX_MSG_CALL CCmdTarget:*AFX_PMSG)(void);struct AFX_MSGMAP_ENTRY UINT nMessage; UINT nCode; UINT nID; UINT nLastID; UINT nSig; AFX_PMSG pfn;struct AFX_MSGMAP const AFX_MSGMAP* pBaseMap; const AFX_MSGMAP_ENTRY* lpEntries;可以看出DECLARE_MESSAGE_MAP宏在其类中申请了一个全局结构和获得该结构的函数,而在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间填写刚才的全局结构,将消息和对应的处理函数联系起来,并通过AFX_MSGMAP中的pBaseMap指针,将各类按继承顺序连接起来,从而提供消息流动的道路(即消息的直流,满足标准消息流动的要求)。下面我们举个例子: CMyWnd : public CWnd DECLARE_MESSAGE_MAP()BEGIN_MESSAGE_MAP(CMyWnd,CWnd) ON_WM_CREATE() ON_WM_PAINT()END_MESSAGE_MAP()被展开后,代码如下: CMyWnd:public CWnd private: static const AFX_MSGMAP_ENTRY _messageEntries;protected: static AFX_DATA const AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const;const AFX_MSGMAP* CMyWnd:GetMessageMap() const return &CMyWnd:messageMap;AFX_DATADEF const AFX_MSGMAP CMyWnd:messageMap=&CWnd:messageMap, &CMyWnd:_messageEntries0;const AFX_MSGMAP_ENTRY CMyWnd:_messageEntries=WM_CREATE,0,0,0,AfxSig_is,(AFX_PMSG)(AFX_PMSGW)(int(AFX_MSG_CALL CWnd:*)(LPCREATESTRUCT)OnCreate,WM_PAINT,0,0,0,AfxSig_vv,(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd:*)(void)OnPaint,0,0,0,0,AfxSig_end,(AFX_PMSG)0;这样 WM_CREATE,WM_PAINT 在消息网中流动,当流到CMyWnd类的 messageMap 结构时,发现有该消息的记录,则调用记录中记载的 OnCreate 和 OnPaint 函数,进行响应消息,从而完成了 Windows 消息驱动机制。 四、MFC消息的起点 我们已经建立了一张消息流动网络,但是消息是怎样从产生到响应函数收到该消息,而且标准消息需要直流,命令消息还有许多拐弯(在标题二中可以看到)。不要紧张,我们只需要看看MFC是怎样实现的。 不管怎么说,对Windows 系统来说都是一样的,它都是不断地用GetMessage(或者其它)从消息队列中取出消息,然后用DispatchMessage将消息发送到窗口函数中去。在窗口类的诞生中知道,MFC将所有的窗口处理函数都注册成DefWndProc,那是不是MFC将所有的消息都发送到DefWndProc中去了呢?很抱歉不是,而是都发送到了AfxWndProc函数去了。你可能要问为什么,这也是我想知道的,那我们就看看MFC代码吧: BOOL CWnd:CreateEx()PreCreateWindow(cs);AfxHookWindowCreate(this);HWND hWnd = :CreateWindowEx();void AFXAPI AfxHookWindowCreate(CWnd *pWnd)pThreadState-m_hHookOldCbtFilter = :SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook,NULL,:GetCurrentThreadId();_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)if(!afxData.bWin31)_AfxStandardSubclass(HWND)wParam);void AFXAPI _AfxStandardSubclass(HWND hWnd)oldWndProc = (WNDPROC)SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)AfxGetAfxWndProc();WNDPROC AFXAPI AfxGetAfxWndProc()return &AfxWndProc;看了上面的代码,不知你有没有了然于胸的感觉啊,原来是这样呀!其实MFC在PreCreateWindow注册窗口类之后,在创建窗口之前,调用了AfxHookWindowCreate函数,该函数设置了钩子(钩子用SetWinowsHook或者SetWindowsHookEx设置,这样消息有满足设置的消息时,系统就发送给你设置的函数,这里是_AfxCbtFilterHook函数),这样每次创建窗口的时候,该函数就将窗口函数修改成AfxWndProc。至于为什么这样做吗?那是为了包容新的3D控件而又同MFC2.5兼容。 五、MFC消息的流动 消息的起点是AfxWndProc函数,所有的消息都被发送到AfxWndProc,也从AfxWndProc再次流向各自的消息响应函数的,怎么流的呢?那只有MFC知道: LRESULT CALLBACK AfxWndProc(.)return AfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);LRESULT AFXAPI AfxCallWndProc()lResult = pWnd-WindowProc(nMsg,wParam,lParam);LRESULT CWnd:WindowProc()if(!OnWndMsg(message,wParam,lParam,&lResult)lResult = DefWindowProc(message,wParam,lParam);BOOL CWnd:OnWndMsg()/该函数原来太过庞大,被我改造了一下,只反映意思,不能执行 if(message = WM_COMMAND) OnCommand(wParam,lParam); if(message = WM_NOTIFY) OnNotify(wParam,lParam,&lResult); pMessage = GetMessageMap(); for(; pMessageMap!=NULL; pMessageMap = pMessageMap-pBaseMap) if(lpEntry=AfxFindMessageEntry(pMessageMap-lpEntries,message,0,0)!=NULL) break; (this-*(lpEntry-pnf)();/调用消息响应函数AFX_MSGMAP_ENTRY AfxFindMessageEntry() while(lpEntry-nSign!=AfxSig_end) if(lpEntry-nMessage=nMsg&lpEntry-nCode=nCode&nID=lpEntry-nID &nIDnLastID) return lpEntry; lpEntry+; 消息被发送到对应窗口的OnWndMsg后,然后根据消息的类型采取相应动作:如果是标准消息,则检查但前类中有无处理函数(由AfxFindMessageEntry实现),若没有,就在其父亲类中找(通过pMessageMap-pBaseMap实现),这样望上顺序搜索消息网,搜索结束也找不到处理函数,那么回到WindowProc函数调用默认DefWindowProc函数;如果是命令消息或通知消息则发送到OnCommand或者OnNotify函数中去处理,来实现消息的拐弯流动: BOOL CWnd:OnCommand(WPARAM wParam,LPARAM lParam) return OnCmdMsg(nID,nCode,NULL,NULL);BOOL CFrameWnd:OnCmdMsg() CView* pView = GetActiveView(); if(pView!=NULL&pView-OnCmdMsg() /相当于图1中Frame指向View的箭头return TRUE; if(CWnd:OnCmdMsg() /图1中Frame自身 return TRUE; CWinApp *pApp = AfxGetApp(); if(pApp != NULL & pApp-OnCmdMsg() /图1中CWinApp对象 return TRUE; ret

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论