用VC进行COM编程所必须掌握的理论知识

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

1 COM实际上是一个C++,而接口都是类,组件从接口派生而

可以简单的用粹的C++法形式来描述COM是个什么东西:
class IObject
{
public:
virtual Function1(...)
= 0;
virtual Function2(...) = 0;
....
};
class MyObject : public IObject
{
public:
virtual Function1(...){...}
virtual Function2(...){...}
....
};

看清楚了IObject就是我的接口,MyObject就是所COM件。切接口都
   
,它所包含的函数都是虚函数,而且它没有成员变量。而COM件就是从
   
类继承下来的派生,它实现些虚函数,此而已。从上面也可以看出,COM件是以
    C++
的,特重要的是虚函数和多性的概念,COM中所有函数都是虚函数,都必
   
虚函数表VTable用,一点是无比重要的,必需刻牢在心

 

2 COM组件有三个最基本的接口类,分别是IUnknownIClassFactoryIDispatch

l         IUnknown

COM定任何件、任何接口都必IUnknownIUnknown包含三个函数,分QueryInterfaceAddRefRelease

QueryInterface用于查询组实现的其它接口,白了也就是看看件的父
   
有哪些接口类。

AddRef用于增加引用

Release用于减少引用

引用概念

COM中的一个非常重要的概念。大体上简单的说来可以这么理解,COM组件是个DLL,当客户程序要用它时就要把它装到内存里。另一方面,一个组件也不是只给你一个人用的,可能会有很多个程序同时都要用到它。但实际上DLL只装载了一次,即内存中只有一个COM组件,那COM组件由谁来释放?由客户程序吗?不可能,因为如果你释放了组件,那别人怎么用,所以只能由COM组件自己来负责。所以出现了引用计数的概念,COM维持一个计数,记录当前有多少人在用它,每多一次调用计数就加一,少一个客户用它就减一,当最后一个客户释放它的时侯,COM知道已经没有人用它了,它的使用已经结束了,那它就把它自己给释放了。

    引用数是COM程里非常容易出的一个地方,但所幸VC的各类库里已基本上AddRef给隐含了,在我的印象里,我程的从来没有AddRef,我只需在适当的Release。至少有两个侯要Release,第一个是用了 QueryInterface以后,第二个是用了任何得到一个接口的指的函数以后,住多MSDN以确定某个函数内部是否用了AddRef,如果是的Release任就要你了。 IUnknown三个函数的实现非常范但也非常烦琐,容易出,所幸的事我可能永不需要自己来实现

l        IClassFactory

    IClassFactory的作用是COM。我知道COM实际上就是一个,那我常是怎么实例化一个类对象的?是用‘new’命令!简单吧,COM件也一如此。但是new它呢?不可能是客程序,因程序不可能知道件的名字,如果客知道件的名字那件的可重用性就要打个大大的折扣了,事上客程序只不知道一个代表件的128位的数字串而已,个等会再介。所以客无法自己件,而且考下,如果件是在程的机器上,你new出一个?所以件的任交了一独的象,象就是厂。件都必有一个与之相厂,厂知道么样创件,当客户请求一个象的实际求交厂,由例,然后把例指程序。程在跨程及用,因为这时就不是一个简单new操作就可以的了,它必经过调度,而复杂的操作都交给类象去做了

    IClassFactory最重要的一个函数就是CreateInstance名思就是例,一般情况下我不会直接用它,API函数都封装好它了,只有某些特殊情况下才会由我自己来用它,也是VCCOM件的好,使我有了更多的控制机会,而VB们这样的机会是太少太少了

l         IDispatch

    IDispatch叫做度接口。它的作用何在呢?个世上除了C++有很多言,比如VB VJVBScriptJavaScript等等。可以这么说,如果世上没有这么多乱七八糟的言,那就不会有IDispatch:-) 知道COM件是C++,是靠虚函数表来用函数的,VC毫无问题本来就是针对C++设计的,以前VB不行,VB也可以用指了,也可以VTable用函数了,VJ也可以,但是有些言不行,那就是脚本言,典型的如VBScriptJavaScript。不行的原因在于它并不支持指都不能用用多性啊,么调这些虚函数啊。唉,没法,也不能置些脚本言于不吧,在网用的都是些脚本言,而分布式用也是COM件的一个主要市,它不得不被些脚本言所用,既然虚函数表的方式行不通,我只能另他法了。时势造英雄,IDispatch运而生。:-)  度接口一个函数一个属性都上号,客程序要些函数属性的侯就把传给IDispatch接口就行了,IDispatch再根据用相的函数,此而已。当然实际这复杂,当一个号就能让别人知道怎么调用一个函数那不是天方夜潭,你让别人知道你要用的函数要参数,参数型什以及返回什西吧,而要以一种统一的方式来问题是件很疼的事

    IDispatch接口的主要函数是Invoke,客程序都用它,然后Invoke用相的函数,如果看一看MS类库实现Invoke的代就会惊实现复杂了,因你必参数型的情况,所幸我需要自己来做件事,而且可能永也没这样的机会。:-)

 

3 dispinterface接口、Dual接口以及Custom接口

l        Dual接口

    的自化接口就是用IDispatch实现的接口。我经讲IDispatch的作用了,它的好就是脚本言象VBScript JavaScript也能用COM件了,从而基本上做到了与言无它的缺点主要有两个,第一个就是速度慢效率低。而易的,通虚函数表一下子就可以用函数了,而通Invoke等于中间转了道手,尤其是需要把函数参数转换成一种规范的格式才去用函数,耽了很多时间。所以一般若非是迫不得已我都想用VTable的方式用函数以得高效率。第二个缺点就是只能使用定好的所的自化数据型。如果不用IDispatch可以想用什数据型就用什么类型,VC会自动给生成相。而用自化接口就不行了,因Invoke实现VC事先写好的,而它不能事先料到我要用到的所有型,它只能根据一些常用的数据型来写它的理代,而且它也要考不同言之的数据转换问题。所以VC化接口生成的度代只适用于它所定好的那些数据型,当然些数据型已丰富了,但不能足自定数据构的要求。你也可以自己写度代理你的自定数据构,但并不是一件容易的事。IDispatch种种缺点(有一个缺点,就是使用麻:-) )在一般都推荐写双接件,称dual接口,实际上就是从IDispatch承的接口。我知道任何接口都必IUnknown承,IDispatch接口也不例外。那从IDispatch承的接口实际上就等于有两个基,一个是IUnknown,一个是IDispatch,所以它可以以两方式来件,可以通IUnknown用虚函数表的方式用接口方法,也可以通IDispatch::Invoke度来就有了很大的灵活性,件既可以用于C++境也可以用于脚本言中,同时满了各方面的需要。

l        dispinterface接口

    dispinterface是一种纯粹的自化接口,可以简单的就把它看作是IDispatch接口(然它实际上不是的)这种接口就只能通化的方式来用,COM件的事件一般都用的是这种形式的接口

l        Custom接口

    Custom接口就是从IUnknown接口派生的然它就只能用虚函数表的方式用接口

 

4COM组件有三种,进程内、本地、远程。对于后两者情况必须调度接口指针及函数参数

COM是一个DLL,它有三运行模式。它可以是程内的,即和用者在同一个程内,也可以和用者在同一个机器上但在不同的程内,可以根本就和用者在两台机器上。里有一个根本点需要牢,就是COM件它只是一个DLL,它自己是运行不起来的,必有一个程象父般照它才行,即COM件必在一个程内.充当看人的任呢?说说调度的问题度是个复杂问题,以我的知识还讲不清楚问题,我只是一般性的谈谈几个最基本的概念。我知道WIN32程序,程都4GB的虚地址空程都有其各自的址,同一个数据在不同的程里的址很可能就是不一的,所以存在着的地址转换问题就是问题于本地和程来DLL和客程序在不同的址空,所以要传递接口指到客程序必经过调度。Windows提供了成的度函数,就不需要我自己来做复杂的事情了。对远件来函数的参数传递是另外一种调度。DCOM是以RPC的,要在网络间传递数据必遵守准的网上数据传输协议,数据传递前要先打包,传递到目的地后要解包,程就是度,程很复杂,不Windows把一切都做好了,一般情况下我不需要自己来DLL

们刚说过一个COM件必在一个程内。于本地模式的件一般是以EXE的形式出所以它本身就已是一个程。DLL,我找一个程,程必包含了  度代实现基本的度。程就是dllhost.exeCOMDLL代理。实际上在  分布式用中,我们应该MTS来作DLL代理,因MTS有着很大的功能,是专门的用于  管理分布式DLL件的工具

度离我很近又似乎很,我们编很少注到它,也是COM的一个点之一,既平  台无性,无你是程的、本地的程内的,程是一的,一切细节都由COM自己理好了,所以我也不用深究问题,只要有个概念就可以了,当然如果你对调度有自己特殊的要求就需要深入了解度的整个程了,里推荐一本《COM+内幕》,这绝对是一本讲调度的好

 

5COM组件的核心是IDL

    希望件是一块块拼装出来的,但不可能是没有定的胡乱拼接,是要遵守一定的准,各个模如何才能密无的合作,必要事先共同制好它交互的范,范就是接口。我知道接口实际上都是,它里面定好了很多的虚函数,等着某个件去实现它,个接口就是两个完全不相的模够组合在一起的关键试想一下如果我是一个件厂商,我件中需要用到某个模,我没有时间自己开发,所以我想到市上找一找看有没有这样的模,我去找呢?也需要的个模界已有了准,已有人制好了准的接口,有很多件工具厂商已在自己的件中实现个接口,那我们寻找的目就是些已经实现了接口的件,我件从哪来,它有什其它的功能,我心它是否很好的实现了我好的接口。这种接口可能是界的准,也可能只是你和几个厂商之内部制协议,但之它是一个准,是你的件和人的模够组合在一起的基,是COM通信的

    COM具有言无性,它可以用任何写,也可以在任何言平台上被用。但至今 止我一直是以C++境中COM,那它的言无性是怎出来的呢?或者话说,我才能以言无的方式来定接口呢?前面我是直接用的方式定的,但然是不行的,除了C++谁还认它呢?正是出于这种,微决定采用IDL来定接口。白了,IDL实际上就是一大家都认识言,用它来定接口,不放到哪个言平台上都认识它。我可以想象一下理想的准的件模式,我们总是从IDL始,先用IDL好各个接口,然后把实现接口的任分配不同的人,有的人可能善VC,有的人可能善VB系,作为项负责人我不些,我只心你最后把DLL我。是一好的开发模式,可以用任何言来开发,也可以用任何言也欣你的开发成果

   

6 COM组件的运行机制,即COM是怎么跑起来的。

部分我将构造一个COM件的最小框架构,然后看一看其内部理流程是怎
       IUnknown *pUnk=NULL;
       IObject *pObject=NULL;
       CoInitialize(NULL);
       CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown,
(void**)&pUnk);
       pUnk->QueryInterface(IID_IOjbect, (void**)&pObject);
       pUnk->Release();
       pObject->Func();
       pObject->Release();
       CoUninitialize();

就是一个典型的COM件的框架,不我的趣在CoCreateInstance身上,来看看它内部做了一些什事情。以下是它内部实现的一个:
       CoCreateInstance(....)
      
{
    .......
    IClassFactory *pClassFactory=NULL;
    CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory,
        (void **)&pClassFactory);
    pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
    pClassFactory->Release();
    ........
       }

的意思就是先得到象,再通过类件从而得到IUnknown

继续深入一,看看CoGetClassObject的内部伪码
      CoGetClassObject(.....)
      {
   //
过查注册表CLSID_Object,得知DLL的位置、文件名
   //
装入DLL
   //
使用函数GetProcAddress(...)得到DLL中函数DllGetClassObject的函数指
  
//DllGetClassObject
      }

DllGetClassObject是干什的,它是用来象的。只有先得到厂才能去.
      
下面是DllGetClassObject伪码
       DllGetClassObject(...)
       {
    ......
    CFactory* pFactory= new CFactory; //

    pFactory->QueryInterface(IID_IClassFactory, (void**)&pClassFactory);
    //
查询IClassFactory
    pFactory->Release();
    ......
       }
       CoGetClassObject
的流程已到此止,在返回CoCreateInstance,看看CreateInstance
      
伪码
       CFactory::CreateInstance(.....)
       {
    ...........
    CObject *pObject = new CObject; //

    pObject->QueryInterface(IID_IUnknown, (void**)&pUnk);
    pObject->Release();
    ...........
       }

 

7 一个典型的自注册的COM DLL所必有的四个函数
  DllGetClassObject:用于厂指
  DllRegisterServer:注册一些必要的信息到注册表中
    DllUnregisterServer:注册信息
     DllCanUnloadNow:闲时个函数,以确定是否可以卸DLL
    DLL
有一个函数是DllMain,个函数在COM中并不要求一定要实现它,但是在VC生成的
  件中自都包含了它,它的作用主要是得到一个全局的

 

8 注册表在COM中的重要作用
    首先要知道GUID的概念,COM中所有的、接口、都用GUID来唯一标识GUID是一个128位的字串,根据特制算法生成的GUID可以保是全世界唯一的。COM件的建,查询接口都是通注册表行的。有了注册表,用程序就不需要知道件的DLL文件名、位置,只需要根据CLSID就可以了。当版本升侯,只要改一下注册表信息就可以神不知鬼不到新版本的DLL

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一 组件基础 1 软件开发的阶段 1.1 结构化编程 采用自顶向下的编程方式,划分模块 和功能的一种编程方式。 1.2 面向对象编程 采用对象的方式,将程序抽象成类, 模拟现实世界,采用继承、多态的方式 设计软件的一种编程方式。 1.3 面向组件编程 将功能和数据封装成二进制代码,采用 搭积木的方式实现软件的一种编程方式。 2 组件和优点 2.1 组件 - 实际是一些可以执行的二进 制程序,它可以给其他的应用程序、操 作系统或其他组件提供功能 2.2 优点 2.2.1 可以方便的提供软件定制机制 2.2.2 可以很灵活的提供功能 2.2.3 可以很方便的实现程序的分布式 开发。 3 组件的标准 - COMComponent Object Model ) 3.1 COM是一种编程规范,不论任何开发语言 要实现组件都必须按照这种规范来实现。 组件和开发语言无关。 这些编程规范定义了组件的操作、接口的 访问等等。 3.2 COM接口 COM接口是组件的核心,从一定程度上 讲"COM接口是组件的一切". COM接口给用户提供了访问组件的方式. 通过COM接口提供的函数,可以使用组件 的功能. 4 COM组件 4.1 COM组件-就是在Windows平台下, 封装在动态库(DLL)或者可执行文件(EXE) 中的一段代码,这些代码是按照COM的 规范实现. 4.2 COM组件的特点 4.2.1 动态链接 4.2.2 与编程语言无关 4.2.3 以二进制方式发布 二 COM接口 1 接口的理解 DLL的接口 - DLL导出的函数 类的接口 - 类的成员函数 COM接口 - 是一个包含了一组函数指针 的数据结构,这些函数是由组件实现的 2 C++的接口实现 2.1 C++实现接口的方式,使用抽象类 定义接口. 2.2 基于抽象类,派生出子类并实现 功能. 2.3 使用 interface 定义接口 interface ClassA { }; 目前VC中,interface其实就是struct 3 接口的动态导出 3.1 DLL的实现 3.1.1 接口的的定义 3.1.2 接口的实现 3.1.3 创建接口的函数 3.2 DLL的使用 3.2.1 加载DLL和获取创建接口的函数 3.2.2 创建接口 3.2.3 使用接口的函数 4 接口的生命期 4.1 问题 在DLL中使用new创建接口后,在用户 程序使用完该接口后,如果使用delete 直接删除,会出现内存异常. 每个模块有自己的内存堆(crtheap) EXE - crtheap DLL - crtheap new/delete/malloc/free默认情况 下都是从自己所在模块内存堆(crtheap) 中分配和施放内存.而各个模块的 这个内存堆是各自独立.所以在DLL中 使用new分配内存,不能在EXE中delete. 4.2 引用计数和AddRef/Release函数 引用计数 - 就是一个整数,作用是 表示接口的使用次数 AddRef - 增加引用计数 +1 Release - 减少引用计数 -1, 如果 当引用计数为0,接口被删除 4.3 使用 4.3.1 创建接口 4.3.2 调用AddRef,增加引用计数 4.3.3 使用接口 4.3.4 调用Release,减少引用计数 4.4 注意 4.4.1 在调用Release之后,接口指针 不能再使用 4.4.2 多线程情况下,接口引用计数 要使用原子锁的方式进行加减 5 接口的查询 5.1 每个接口都具有唯一标识 GUID 5.2 实现接口查询函数 QueryInterface 6 IUnknown 接口 6.1 IUnknown是微软定义的标准接口 我们实现所有接口就是继承这个接口 6.2 IUnknown定义了三个函数 QueryInterface 接口查询函数 AddRef 增加引用计数 Release 减少引用计数 7 接口定义语言 - IDL(Interface Definition Language ) 7.1 IDL和MIDL IDL - 定义接口的一种语言,与开发 语言无关. MIDL.EXE - 可以将IDL语言定义接口, 编译成C++语言的接口定义 7.2 IDL的基础 import "XXXX.idl" [ attribute ] interface A : interface_base { } 7.2.1 Import 导入,相当于C++的 #include 7.2.2 使用"[]"定义区域,属性描述 关键字 1) object - 后续是对象 2) uuid - 定义对象GUID 3) helpstring - 帮助信息 4) version - 版本 5) point_default - 后续对象 中指针的默认使用方式 比如: uniqune - 表示指针可以 为空,但是不能修改 7.2.3 对象定义 1) 父接口是IUnknown接口 2) 在对象内添加函数,函数定义必须 是返回 HRESULT. HRESULT是32位整数,返回函数是否 执行成功,需要使用 SUCCESSED和 FAILED宏来判断返回值.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值