注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

重读深入浅出MFC(5)-消息处理函数的初步探索  

2011-07-19 17:12:32|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

在SDK函数里,我们会自己写一个消息的处理函数CALLBACK WINPROC(),然后在这个函数里截获WM_CHAR等消息,然后再处理其参数,达到自己的目的,在MFC里面是怎么样的呢?
先简单的剖析一下,等到后面(具体说第九章)会有进一步的详细说明.
MFC在创建窗口时会调用CWnd::CreateEx()这个函数,这个函数会调用:
PreCreateWindow(cs);
AfxHookWindowCreate(this); //
HWND hWnd = ::CreateWindowEx();//大家可以在VC的程序里将动态库改成静态库,这样就可以直接跟到MFC的源码里了,

                                   // 但是,大多数的源码是保密的,所以我们只能看到一部分.管窥见豹吧.
AfxHookWindowCreate(this); 这个东东,在第九章,侯大人讲的,我们直接拿过来,因为我们是重读,很多东西就不需要一步步的走了,要高度的总结和融会贯通.
在CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)里宏展开后会调用AfxEndDeferRegisterClass(short fClass),在这个函数里
WNDCLASS wndcls;
        memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
   wndcls.lpfnWndProc = DefWindowProc;
   wndcls.hInstance = AfxGetInstanceHandle();
   wndcls.hCursor = afxData.hcurArrow;
看到没,跟SDK有啥区别,有木有,有木有?wndcls.lpfnWndProc = DefWindowProc;这行,不就直接调用到默认的处理消息函数么,大功告成了么?不能这么简单的想,微软不简单,MFC更不简单.这里还要说句题外话,WNDCLASS类和创建窗口,各有分工,不要搞混喽.
这时候儿就应该跳到第九章的内容了,看到AfxHookWindowCreate(this); 这个函数没,他就是罪魁祸首,
// in WINCORE.CPP(MFC 4.x)
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
...
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
...
}
在_AfxCbtFilterHook钩子函数中(WIN中经常会利用hook 和subclassing,也就是钩子和子类化,有兴趣可以自己看看,有时间我会总结一下,钩子干坏事比较多,子类化美化界面用得比较多)调用_AfxStandardSubclass((HWND)wParam),在这个函数里又调用oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)AfxGetAfxWndProc());然后调用
WNDPROC AFXAPI AfxGetAfxWndProc()
{...
return &AfxWndProc;
}
这样子,在一系列的配合下,MFC完成了处理消息函数的转换工作.晕了,真晕了.
然后下来就是按照这个方向来处理:AfxWndProc---AfxCallWndProc---WindowProc---DefWindowProc
LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
if (m_pfnSuper != NULL)
return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
WNDPROC pfnWndProc;
if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)
return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
else
return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
}

这样,基本上处理的一个过程就OK了.
顺便把OnIdle这个函数复习一下,这个函数很有用,大家有时候儿考虑一些后台的东西时,或者一些不太占用长时间的东东时候可以考虑手这个函数来处理一下.
CWinApp::OnIdle  (其实这个函数在MFC是调用CThread::OnIdle()的,这个函数在THRDCORE.CPP 和APPCORE.CPP里找到,在你的VS安装目录下.大家可以多看源码,有好处的)

virtual   BOOL   OnIdle(   LONG   lCount   );  

返回值:如果要接收更多的空闲处理时间,则返回非零值;如果不需要更多的空闲时间则返回0。  

参数:   lCount   该参数是一个计数值,当应用程序的消息队列为空,OnIdle函数被调用时,该计数值就增加1。每当一条新消息被处理时,该计数值就被复位为0。你可以使用

lCount参数来确定应用程序不处理消息时空闲时间的相对长度。      

说明:  
如果要执行空闲时处理,则重载这个成员函数。当应用程序的消息队列为空时,OnIdle就在缺省的消息循环中被调用。你可以用重载函数来调用自己的后台空闲处理任务。  
OnIdle应返回0以表明不需要更多的空闲处理时间。当消息队列为空时,OnIdle每被调用一次lCount参数就增加,而每处理一条新消息lCount就被复位为0。你可以根据这个计数值

调用不同的空闲处理例程。  
下面总结了空闲循环处理:  
1. 如果微软基础类库中的消息循环检查消息队列并未发现没有未被处理的消息,它就为应用程序对象调用OnIdle函数,并将lCount参数设为0。      
2. OnIdle执行一些处理,然后返回一个非零值,表示它还需要被调用,以进行进一步处理。      
3. 消息循环再次检查消息队列。如果没有未处理的消息,则再次调用OnIdle,增加lCount参数。      
4. 最后,OnIdle结束所有的空闲任务并返回0。这就告诉消息循环停止调用OnIdle直到在消息队列中接收到下一条消息为止,在那时,空闲循环将重新启动,而参数被设为0。    

 
因为只有在OnIdle返回之后应用程序才能处理用户输入,因此在OnIdle中不应进行较长的任务。  

注意:  
OnIdle的缺省实现更新命令用户接口对象,如菜单项和工具条等,还实现了内部数据结构的清理。因此,如果你重载了OnIdle,你必须用重载版本中使用的lCount值来调用

CWinApp::OnIdle。首先调用所有基类的空闲处理(即直到基类的OnIdle返回0)。如果你需要在基类处理完成之前进行一些工作,则应回顾基类的实现以在自己的工作期间选择一

个合适的lCount值。  

示例:  
下面的两个例子演示了OnIdle的用法。  

第一个例子处理两个空闲任务,用lCount参数来排列这些任务的优先权。第一个任务优先权较高,一旦可能你就应当执行此任务。第二个任务不十分重要,只有当用户输入有一个

较长时间的间歇的时候才应执行此任务。注意其中对基类的OnIdle的调用。第二个例子管理着一组具有不同优先权的空闲任务。  
BOOL   CMyApp::OnIdle(LONG   lCount)  
{  
 BOOL   bMore   =   CWinApp::OnIdle(lCount);  
 if   (lCount   ==   0)  
 {  
  TRACE("App   idle   for   short   period   of   time\\n");  
  bMore   =   TRUE;  
 }  

 else   if   (lCount   ==   10)  
 {  
  TRACE("App   idle   for   longer   amount   of   time\\n");  
  bMore   =   TRUE;  
 }  

 else   if   (lCount   ==   100)  
 {  
  TRACE("App   idle   for   even   longer   amount   of   time\\n");  
  bMore   =   TRUE;  
 }  

 else   if   (lCount   ==   1000)  
 {  
  TRACE("App   idle   for   quite   a   long   period   of   time\\n");  
  //   bMore   没有被设为TRUE,   不在需要空闲  
  //   重要:bMore   没有被设为   FALSE,因为   CWinApp::OnIdle可能还有其它空闲任务要完成。  
 }  
 return   bMore;   //   返回TRUE,只要还有其它空闲任务  
}  

第二个示例:  
//   在这个例子中,有四个空闲循环任务,它们被赋予  
//   不同的优先权,运行的机会不同:  
//   Task1在空闲时总能运行,要求在框架处理它自己的空闲循环任务时没有消息在等候。(lCount为0或1)  
//   Task2   仅当Task1以及运行时才能运行,要求当Task1运行时没有消息在等候。  
//   Task3和Task4仅当Task1和Task2都运行之后才能运行,  
//   并且在此期间没有消息在等候。如果Task3能够运行,  
//   则Task4总是在Task3之后立即运行。  
BOOL   CMyApp::OnIdle(LONG   lCount)  
{  
 //   在这个例子中,像多数应用程序一样,你应该让基类  
 //   的CWinApp::OnIdle在你试图进行任何附加的空闲循环  
 //   过程之前完成它的处理。  
 if   (CWinApp::OnIdle(lCount))   return   TRUE;  
 //   基类的CWinApp::OnIdle为lCount保留0和1给框架自己的  
 //   空闲处理使用。如果你希望与框架平等地共享空闲处理  
 //   时间,则应替换上面的if语句,直接调用CWinApp::OnIdle,  
 //   然后为lCount的值0和/或1加入一个case语句。首先应当研  
 //   究基类的实现以理解你的空闲循环任务将会如何与框架的  
 //   空闲循环处理竞争。  
 switch   (lCount)  
 {  
 case   2:  
  Task1();  
  return   TRUE;   //   下一次给   Task2   一个机会  
 case   3:  
  Task2();  
  return   TRUE;   //   下一次给Task3和Task4一个机会  
 case   4:  
  Task3();  
  Task4();  
  return   FALSE;   //   再次回到空闲循环任务  
 }  
 return   FALSE;  
}

  评论这张
 
阅读(699)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017