[开发总结]MFC/COM技术应用篇(八)

一、温故而知新—MFC框架

    1)RTTI & Dynamic Create

[开发总结]MFC/COM技术应用篇(八)_第1张图片

简要说明:

----------------------------------------------------------------------------------------------

 以上图用于说明MFC两项关键技术:RTTI(Runtime Type Information) 运行时类型识别和Dynamic Create对象动态生成。

实现机理:

       CRuntimeClass该结构为MFC内建类型,其主要作用:

        定义一个用户类型时,利用该结构记录该用户类型名和对象创建函数地址。系统中所有的用户类型信息通过该结构保存起来,并形成“类型识别网”(实际上就是链表)。

       如果一个用户类型的.h.CPP文件中加入了DECLARE_DYNCREATE()IMPLEMENT_DYNCREATE()宏,则相当于:

        1.创建了一个的CRuntimeClass结构类型的全局实例(名为:class##类名)。

        2.该实例记录了该用户类型的两项重要信息:

                   用户类型名(m_lpszClassName

                   对象创建函数指针(m_pfnCreateObject

         3.利用指针m_pBaseClass指向基类的“类型识别对象”(classBaseClass),利用m_pNextClass指向下一个“类型识别对象”,这样就将该用户类型的“类型识别对象”加入到全局的“类型识别网”(链表)中。

  4 .“类型识别网”(链表)的首指针为 :CRuntimeClass::pFirstRuntimClass ,此指针变量为全局类型。系统通过该全局指针遍历“类型识别网”(链表)。
RTTI 类型识别:
       遍历“类型识别网”(链表),比较 CRuntimeClass 类型指针或类型名 lpszClassName). 如: pObj->IsKindOf(RUNTIME_CLASS(CMyClass));
       
       Dynamic Create 动态创建:
              遍历“类型识别网”(链表),通过比较类型名( m_lpszClassName ),找到对应的对象创建函数指针 (m_pfnCreateObject) ,动态创建对象,返回“基类指针”。( 创建对象时,只需“类型识别网”中提供的信息,不依赖任何类型信息)
 
       以上与实际的应用可能存在细节上的出入,但大致的原理应该是这样。
   
        2)Message Mapping

          传统SDK程序的消息循环 
          在传统的SDK程序中,消息循环是很简单的,
在WinMain 中 CreateWindow通过一个参数将创建 的 窗口和窗口类联系起来,这样该窗口的所有消息都将发送到该窗口类的窗口函数WndProc,其后
WndProc根据不同的消息给予不同的动作。

             MFC期望的消息循环
       在传统的SDK程序中消息循环是非常简单的,并且将窗口和窗口函数绑定在一起。而在MFC中就出现了 问题,比如CDocument类,不是窗口,所以没有窗口类,但是我也想让它响应消息,怎办?问题不仅仅 如此,我们再看看MFC的消息,就会发现更多问题。
     
           MFC 将消息分为三大类:
              1 .标准消息,如 WM_ 开头,任何派生自 CWnd 的类都可以接受该消息
              2 .命令消息,即 WM_COMMAND ,任何派生自 CCmdTarget 的类,兼可接受该消息。
              3 .通知类消息, Control Notification ,也以 WM_COMMAND 形式出现,由控件产生,通知其
                   父窗口。
       
          MFC中,所有能够接受消息的类都必须继承于CCmdTarget类,因为这些类都一个共同的特征:
         含有DECLARE_MESSAGE_MAPBEGIN_MESSAGE_MAPEND_MESSAGE_MAP三个宏
      (宏的定义,在MFC中中查看很方便,这里不具体列出),就这三个宏组织了一张庞大的消息映射
        网。如下图:
                 

          从上图可以看出DECLARE_MESSAGE_MAP和BEGIN_MESSAGE_MAPEND_MESSAGE_MAP三个宏的作用,其实就是创建了两个全局数据结构:

     “消息映射数组”:将消息和对应的处理函数记录下来。

      “映射表”:通过AFX_MSGMAP中的pBaseMap指针,将各类按继承顺序连接起来,从而形成消息映射网,以提供消息流动的道路。

        我们已经建立了一张消息流动网络,但是消息是怎样从产生到响应函数收到该消息? 不管怎么说,对  Windows 系统来说都是一样的,它都是不断地用 GetMessage (或者其它)从消息队列中取出消息,然后用 DispatchMessage 将消息发送到窗口函数中去。在 " 窗口类的诞生 " 中知道, MFC 将所有的窗口处理函数都注册成 DefWndProc ,那是不是 MFC 将所有的消息都发送到 DefWndProc 中去了呢?很抱歉不是,而是都发送到了 AfxWndProc 函数去了。你可能要问为什么,这也是我想知道的,那我们就看看下面的序列图中窗口的创建过程吧:

 
      消息的起点是 AfxWndProc 函数,所有的消息都被发送到 AfxWndProc ,也从 AfxWndProc 再次流向各自的消息响应函数的,怎么流的呢?看看下图就 知道了:

如果是“标准消息(WM_XXXX)”,则在CWnd::OnWndMsg函数内,直接发送到接收消息的目标窗口。
如果是"通知消息",则转到CCmdTarget::OnCmdMsg函数中(其内的GetMessageMap为虚函数),对自身的映射表中查找,简图如下:

[开发总结]MFC/COM技术应用篇(八)_第2张图片


图1 消息的拐弯流动
--------------------
二、自动化(Automation)对象———组件技术应用。
自动化,是基于COM技术的一个高级应用。
自动化的本质(最简单的理解):
是继承了IDispatch接口的接口。
说到底,自动化就是为了消除编译期的类型信息依赖性(COM也有CLSID/IID/tlb/h依赖性),
而通过“名字”来晚期(运行时)绑定函数地址和参数类型校验的技术。通常的COM就是通过内存中虚表来晚期帮定,但其编译时仍然需要导入类型信息(#import).
有了自动化这一技术后,在脚本语言和解释执行语言中,在不需要任何类型信息的情况下,就可以方便调用自动化对象。仅仅知道ProgID(字符串)就够了(ProgID = “组件名.类名”)
创建自动化对象:
在MFC中,为应用自动化技术提供了很大的便利,使用起来非常简单。
1。创建MFC的RegularDLL时,勾选"Automation"
2。创建一个用户类,继承于CCmdTarget。创建完后,MFC系统自动生成一个接口dispinterface,其信息保存在odl文件(与COM的idl文件类似),同时生成对应该接口的实现类,该类名与接口同名(接口与实现类是一对一关系)。他们之间的关联估计是通过宏或MFC内部机制来实现。
3。余下就是声明和实现接口,就这样,一个自动化对象创建完成了。
调用自动化对象:
在VC6.0下调用通常有两种方式:
1。COleDispatchDriver *pDriver = new ();
    pDriver->CreateDispatch(组件名.类名);
    pDirver->InvokeHelper(函数索引,DISPATCH_METHOD,。。。);
   此调用不存在类型依赖性,而是通过索引或函数名实现晚绑定。
2。生成自动化对象的包装类(COleDispatchDriver).
    在类创建向导窗口中,选择'Automation'页面,后点"Add Class...",选择你所包装自动化的tlb或DLL文件。 确定后,MFC系统自动生成一个继承于COleDispatchDriver的包装类,类名为tlb中的接口名。
MFC直接通过该包装类的函数,每个函数实际上还是调用InvokeHelper,转移到自动化对象中。
关键的地方是该包装类在调用CreateDispatch后,将自动化对象的接口指针保存到包装类的成员m_lpDispatch内,包装类就通过该成员进行调用转移。个人认为,该调用方式,仍然存在“tlb类型依赖性”。只是调用时避开了InvokeHelper那些麻烦参数,显得更简洁而已。
有点罗嗦。呵呵。
关于自动化,<ATL开发指南>书中,讲得清晰易懂.

--------------------- 

待续  ......

你可能感兴趣的:(数据结构,windows,command,mfc,dll,语言)