依睛(IT blog) 我回来了,PHP<-->C/C++ LINUX

笨鸟

统计

积分与排名

友情连接

最新评论

Windows消息的查找和匹配

  1. CWnd或者派生类的对象调用OnWndMsg搜索本对象或者基类的消息映射数组,寻找当前消息的消息处理函数。如果当前对象或者基类处理了当前消息,则必定在其中一个类的消息映射数组中匹配到当前消息的处理函数。
  2. 消息匹配是一个比较耗时的任务,为了提高效率,MFC设计了一个消息缓冲池,把要处理的消息和匹配到的消息映射条目(条目包含了消息处理函数的地址)以及进行消息处理的当前类等信息构成一条缓冲信息,放到缓冲池中。如果以后又有同样的消息需要同一个类处理,则直接从缓冲池查找到对应的消息映射条目就可以了。
  3. MFC用哈希查找来查询消息映射缓冲池。消息缓冲池相当于一个哈希表,它是应用程序的一个全局变量,可以放512条最新用到的消息映射条目的缓冲信息,每一条缓冲信息是哈希表的一个入口。
  4. 采用AFX_MSG_CACHE结构描述每条缓冲信息,其定义如下:
  5. struct AFX_MSG_CACHE
  6. {
  7. UINT nMsg;
  8. const AFX_MSGMAP_ENTRY* lpEntry;
  9. const AFX_MSGMAP* pMessageMap;
  10. };
  11. nMsg存放消息ID,每个哈希表入口有不同的nMsg。
  12. lpEnty存放和消息ID匹配的消息映射条目的地址,它可能是this所指对象的类的映射条目,也可能是这个类的某个基类的映射条目,也可能是空。
  13. pMessageMap存放消息处理函数匹配成功时进行消息处理的当前类(this所指对象的类)的静态成员变量messageMap的地址,它唯一的标识了一个类(每个类的messageMap变量都不一样)。
  14. this所指对象是一个CWnd或其派生类的实例,是正在处理消息的MFC窗口对象。
  15. 哈希查找:使用消息ID的值作为关键值进行哈希查找,如果成功,即可从lpEntry获得消息映射条目的地址,从而得到消息处理函数及其原型。
  16. 如何判断是否成功匹配呢?有两条标准:
  17. 第一,当前要处理的消息message在哈希表(缓冲池)中有入口;第二,当前窗口对象(this所指对象)的类的静态变量messageMap的地址应该等于本条缓冲信息的pMessagMap。MFC通过虚拟函数GetMessagMap得到messageMap的地址。
  18. 如果在消息缓冲池中没有找到匹配,则搜索当前对象的消息映射数组,看是否有合适的消息处理函数。
  19. 如果匹配到一个消息处理函数,则把匹配结果加入到消息缓冲池中,即填写该条消息对应的哈希表入口:
  20. nMsg=message;
  21. pMessageMap=this->GetMessageMap;
  22. lpEntry=查找结果
  23. 然后,调用匹配到的消息处理函数。否则(没有找到),使用_GetBaseMessageMap得到基类的消息映射数组,查找和匹配;直到匹配成功或搜寻了所有的基类(到CCmdTarget)为止。
  24. 如果最后没有找到,则也把该条消息的匹配结果加入到缓冲池中。和匹配成功不同的是:指定lpEntry为空。这样OnWndMsg返回,把控制权返还给AfxCallWndProc函数,AfxCallWndProc将继续调用DefWndProc进行缺省处理。
  25.  
  26. 消息映射数组的搜索在CCmdTarget::OnCmdMsg函数中也用到了,而且算法相同。为了提高速度,MFC把和消息映射数组条目逐一比较、匹配的函数AfxFindMessageEntry用汇编书写。
  27. const AFX_MSGMAP_ENTRY* AFXAPI
  28. AfxFindMessageEntry(const AFX_MSGMAP_ENTRY* lpEntry,
  29. UINT nMsg, UINT nCode, UINT nID)
  30. 第一个参数是要搜索的映射数组的入口;第二个参数是Windows消息标识;第三个参数是控制通知消息标识;第四个参数是命令消息标识。
  31. 对Windows消息来说,nMsg是每条消息不同的,nID和nCode为0。
  32. 对命令消息来说,nMsg固定为WM_COMMAND,nID是每条消息不同,nCode都是CN_COMMAND(定义为0)。
  33. 对控制通知消息来说,nMsg固定为WM_COMMAND或者WM_NOTIFY,nID和nCode是每条消息不同。
  34. 对于Register消息,nMsg指定为0XC000,nID和nCode为0。在使用函数AfxFindMessageEntry得到匹配结果之后,还必须判断nSig是否等于message,只有相等才调用对应的消息处理函数。
  35.  
  36. Windows消息处理函数的调用

    对一个Windows消息,匹配到了一个消息映射条目之后,将调用映射条目所指示的消息处理函数。

    调用处理函数的过程就是转换映射条目的pfn指针为适当的函数类型并执行它:MFC定义了一个成员函数指针mmf,首先把消息处理函数的地址赋值给该函数指针,然后根据消息映射条目的nSig值转换指针的类型。但是,要给函数指针mmf赋值,必须使该指针可以指向所有的消息处理函数,为此则该指针的类型是所有类型的消息处理函数指针的联合体。

    对上述过程,MFC的实现大略如下:

    union MessageMapFunctions mmf;

    mmf.pfn = lpEntry->pfn;

    swithc (value_of_nsig){

    case AfxSig_is: //OnCreate就是该类型

    lResult = (this->*mmf.pfn_is)((LPTSTR)lParam);

    break;

    default:

    ASSERT(FALSE); break;

    }

    LDispatchRegistered: // 处理registered windows messages

    ASSERT(message >= 0xC000);

    mmf.pfn = lpEntry->pfn;

    lResult = (this->*mmf.pfn_lwl)(wParam, lParam);

    如果消息处理函数有返回值,则返回该结果,否则,返回TRUE。

    对于图4-1所示的例子,nSig等于AfxSig_is,所以将执行语句

    (this->*mmf.pfn_is)((LPTSTR)lParam)

    也就是对CTview::OnCreate的调用。

    顺便指出,对于Registered窗口消息,消息处理函数都是同一原型,所以都被转换成lwl型(关于Registered窗口消息的映射,见4.4.2节)。

    综上所述,标准Windwos消息和应用程序消息中的Registered消息,由窗口过程直接调用相应的处理函数处理:

    如果某个类型的窗口(C++类)处理了某条消息(覆盖了CWnd或直接基类的处理函数),则对应的HWND窗口(Winodws window)收到该消息时就调用该覆盖函数来处理;如果该类窗口没有处理该消息,则调用实现该处理函数最直接的基类(在C++的类层次上接近该类)来处理,上述例子中如果CTview不处理WM_CREATE消息,则调用上一层的CWnd::OnCreate处理;

    如果基类都不处理该消息,则调用DefWndProc来处理。

posted on 2008-01-24 17:03 向左向右走 阅读(225) 评论(0)  编辑 收藏 引用 所属分类: C/C++学习资料库

只有注册用户登录后才能发表评论。