分析MFC中的映射

2016-02-19 13:57 12 1 收藏

下面图老师小编跟大家分享分析MFC中的映射,一起来学习下过程究竟如何进行吧!喜欢就赶紧收藏起来哦~

【 tulaoshi.com - 编程语言 】

    条件查找映射

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  MFC中大量使用了BEGIN_XXX_MAP这样的宏,以及映射进行查找优化,例如消息映射,OLE命令映射,以及接口等等。每个映射包含一个指向基类的映射的指针。这样,当一个类需要根据一定的条件查找一个对象时,它会查找本类对象,如果没有找到,那么会查找基类,直到根基类。这类查找包含Windows消息,命令,事件和OLE命令的分发,和对象实现的接口的查询等等。

  下面是函数BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)的部分代码,演示了如何根据消息的ID查找处理函数。

  const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();
  UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);
  AfxLockGlobals(CRIT_WINMSGCACHE);
  AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
  const AFX_MSGMAP_ENTRY* lpEntry;
  if (message == pMsgCache-nMsg && pMessageMap == pMsgCache-pMessageMap)
  {
  // cache hit
  lpEntry = pMsgCache-lpEntry;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  if (lpEntry == NULL)
  return FALSE;

  // cache hit, and it needs to be handled
  if (message 0xC000)
  goto LDispatch;
  else
  goto LDispatchRegistered;
  }
  else
  {
  // not in cache, look for it
  pMsgCache-nMsg = message;
  pMsgCache-pMessageMap = pMessageMap;

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  #ifdef _AFXDLL
  for (/* pMessageMap already init'ed */; pMessageMap-pfnGetBaseMap != NULL;
  pMessageMap = (*pMessageMap-pfnGetBaseMap)())
  #else
  for (/* pMessageMap already init'ed */; pMessageMap != NULL;
  pMessageMap = pMessageMap-pBaseMap)
  #endif
  {
  // Note: catch not so common but fatal mistake!!
  // BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
  #ifdef _AFXDLL
  ASSERT(pMessageMap != (*pMessageMap-pfnGetBaseMap)());
  #else
  ASSERT(pMessageMap != pMessageMap-pBaseMap);
  #endif

 if (message 0xC000)
  {
  // constant window message
  if ((lpEntry = AfxFindMessageEntry(pMessageMap-lpEntries,
  message, 0, 0)) != NULL)
  {
  pMsgCache-lpEntry = lpEntry;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  goto LDispatch;
  }
  }
  else
  {
  // registered windows message
  lpEntry = pMessageMap-lpEntries;
  while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
  {
  UINT* pnID = (UINT*)(lpEntry-nSig);
  ASSERT(*pnID = 0xC000 || *pnID == 0);
  // must be successfully registered
  if (*pnID == message)
  {
  pMsgCache-lpEntry = lpEntry;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  goto LDispatchRegistered;
  }
  lpEntry++; // keep looking past this one
  }
  }
  }

  pMsgCache-lpEntry = NULL;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  return FALSE;
  }

  LDispatch:

  注意:

  1.对查找结果的缓存可以提高查找的效率。

  2.不要被MFC起的名字欺骗了,从数据结构上来说,查找是顺序的,而不是使用CMap类使用的散列技术,所以使用散列技术,缓存最近的查找结果和把最常用的映射项放在最前面通常有助于提高效率。

  使用查找映射的好处是,可以方便地在派生类中扩展和覆盖映射(例如重新实现IDispatch),而不用重写/重载查找函数(消息和命令的分发,或者接口的查询);也可以不使用对资源消耗很大的虚函数表。(尽管如此,CWnd类还是有无数个虚函数,并且不出意外地看到,在MFC6到MFC7的升级中又有增加)

  使用查找映射的坏处么,当然是理解上的问题和性能上的损失了。

句柄映射

  MFC在把句柄封装成对象方面不遗余力,为了保证同一线程内对象-句柄映射是一对一的,创建了各种各样的句柄映射,窗口,GDI对象,菜单诸如此类。为了封装GetDlgItem,SelectObject这样的API返回的临时的句柄,MFC还产生临时的对象-句柄映射。句柄映射使得GetParentFrame这样的函数可以实现。

  CFrameWnd* CWnd::GetParentFrame() const
  {
  if (GetSafeHwnd() == NULL) // no Window attached
  return NULL;

  ASSERT_VALID(this);

  CWnd* pParentWnd = GetParent(); // start with one parent up
  while (pParentWnd != NULL)
  {
  if (pParentWnd-IsFrameWnd())
  return (CFrameWnd*)pParentWnd;
  pParentWnd = pParentWnd-GetParent();
  }
  return NULL;
  }
  
  _AFXWIN_INLINE CWnd* CWnd::GetParent() const
  { ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }

  看到了么,它首先调用API GetParent,然后去本线程的窗口-句柄映射查找对象指针,然后调用CWnd::IsFrameWnd来决定对象是否是框架。(谢天谢地,这个函数是用虚函数而不是用CObject::IsKindOf,不然又得遍历一遍运行时类信息)

  在一些经常调用的函数里面也使用到这个映射

  LRESULT CALLBACK
  AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  {
  // special message which identifies the window as using AfxWndProc
  if (nMsg == WM_QUERYAFXWNDPROC)
  return 1;

 // all other messages route through message map
  CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
  ASSERT(pWnd != NULL);
  ASSERT(pWnd-m_hWnd == hWnd);
  if (pWnd == NULL || pWnd-m_hWnd != hWnd)
  return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
  return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
  }

  也就是说,它要遍历一遍afxMapHWND()返回的对象里面的永久的句柄映射。而这个函数在每个消息到达的时候都要调用。这是MFC应用程序性能损失的原因之一。

  同样的,由于这些对象是被线程所拥有的,MFC的这些句柄映射的存储方式是线程局部存储(thread-local-storage ,TLS)。也就是说,对于同一个句柄,句柄映射中相应的对象可以不一致。这在多线程程序中会造成一些问题,参见微软知识库文章Q147578 CWnd Derived MFC Objects and Multi-threaded Applications。

总结

  MFC为了快速和方便地开发作了很多工作,例如上述的两种映射,但是性能方面有所损失。开发应用程序时,需要在快速方便和性能损失方面的权衡。

来源:http://www.tulaoshi.com/n/20160219/1605797.html

延伸阅读
在非MFC程序中引用CString 作者:阿半 CString在当今软件设计界里还是小有名气的,说它是MFC中使用的最多的类一点也不过,然而在使用sdk编windows程序的时候,确不能利用CString类,只能用sdk的运行时库,比如strlen,strcpy,strstr等等,本文讨论的是在非mfc程序中使用CString类的方法,算是...
MFC中有几个字符串操作函数很有用,但有的在MSDN中都查不到,因为MSDN没有公布。下面我们来看看它们的用法和声明及定义等。 // 功能 : 格式化字符串 // 参数 : rString - 输出参数,格式化后的字符串将保存在此字符串中 !-- frame contents -- !-- /frame contents -- // nIDS - 将进行替换操作的格式字符串...
我们知道,在EasyDBO最后一个测试版本中,增加了使用注解来配置对象及关系表映射的功能。前段时间看到有人在问注解中怎么配置主键属性及主键生成器,由于在开发文档中使用的是默认配置,所以文档中使用了下面的例子: import Java.io.Serializable;  import java.util.Date;import com.easyjf.dbo.annotation....
在MFC程序中添加全屏显示功能 来源:MSDN 翻译整理:冰点工作室 小鹰 下载本文示例源代码 这是工作室为实现全屏显示所发集的一个演示程序。你可以通过选择全屏显示菜单选项时看到相应的效果。这段代码包括两个内容:全屏显示,浮动工具条(用于恢复操作)。 下图便是全屏状态和用于...
摘要: 本文主要介绍了一种在MFC应用程序中实现起来比较方便的浏览PDF、Word格式文档文件的方法,同时给出了具体的编程步骤和较详细的实现代码。 概述 随着网络技术的迅速发展,越来越多的电子图书资料能够更加方便的从网上被检索、下载。其文件格式也不仅限于HTML格式的Web页,PDF、Word等多种格式也逐渐成为网络资料交换的主...

经验教程

573

收藏

52
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部