.NET和COM

1.元数据
在COM中,组件的所有信息都存储在类型库中。类型库包含的信息有接口、方法和参数的名称和ID等。而在.NET中,所有这些信息都可以存储在程序集中,COM存在的问题是,类型库是不能扩展的。在C++中, IDL(Interface Definition Language,接口定义语言)文件用于描述接口和方法。其中一些IDL修饰符不在类型库中,因为Visual Basic(和负责开发类型库的Visual Basic小组)不能使用这些IDL修饰符。而在.NET中,不存在这个问题,因为.NET元数据可以使用自定义特性来扩展。因此,一些COM组件有类型库,而其他COM组件没有。如果没有类型库可用,就可以使用C1头文件来描述接口和方法。在.NET中,使用带有类型库的COM组件比较容易,也可使用不带类型库的COM组件。在这种情况下,必须使用C#代码重新定义COM接口。
2.释放内存
在.NET中,内存的释放由垃圾收集器完成。这完全不同于COM, COM依赖的是引用计数。IUnknown接口是每个COM对象必须实现的一个接口,它提供了3个方法。其中两个方法与引用计数有关。如果需要另一个接口指针,客户端就必须调用AddRefO方法,这个方法会递增引用计数。Release()方法会递减引用计数,如果所得的引用计数是0,就销毁对象,释放内存。
3.接口
接口是COM的核心,它区分了在客户端和对象之间使用的契约和现实方式。接口(协定)定义了有组件提供的方法,可以由客户端使用。而在.NET中,接口也有非常重要的作用。
COM区分3种接口类型:自定义接口、调度接口和双重接口。
4.自定义接口
自定义接口派生自IUnknown接口。因为自定义接口定义了虚拟表中方法的顺序,所以客户端可以直接访问接口的方法。这表示在开发阶段客户端需要能识别虚拟表,因为方法的绑定使用你内存地址进行。因此,自定义接口不能由脚本客户端使用.自定义接口IMath的虚拟表,除了接口IUnknown接口的方法之外,该接口还提供了Add()和Sub()方法。
5调度接口
因为脚本客户端不支持自定义接口,所以需要另一种接口类型,而在调度接口中,可用于客户端可用的接口总是IDispatch接口。IDispatch接口派生自IUnknown接口,除了接口IUnknown接口的方法之外,他还提供了4个方法,其中两个最重要的方法是GetIDsOfNames()和Invoke()。在调度接口中需要两个表。第一个表把方法名或属性名映射到调度ID上,第二个表把调度ID映射到方法或属性的实现代码上。
在客户端调用组件中的方法时,它先调用GetIDsOfName()方法,并给他传递要调用的方法的名称。GetIDsOfName()方法会查找名称ID表,返回ID,客户端再使用这个ID调用Invoke()方法。
6.双重接口
可以想象,调度接口比自定义接口慢得多。另一方面,脚本客户端不能使用自定义接口。双重接口可以解决这个问题。双重接口派生自IDispatch接口,但提供了可以在虚拟表中直接使用的接口的方法。脚本客户端可以使用IDispatch接口调用Add()和Sub()方法,而能够识别虚拟表的客户端可以直接调用Add()和Sub()方法。如果.NET类实现多个接口,就可以进行类型强制转换,得到一个接口或另一个接口。而在COM中,IUnknown接口通过QueryInterface()方法提供了类似的机制。因为IUnknown接口是其他接口的基接口,所以可以以任何方式使用QueryInterface()方法。
7.方法的绑定
客户端映射方法的方式用术语早起绑定和后期绑定来定义。后期绑定表示要调用的方法是在运行期间确定的。.NET使用SystemReflection名称空间来实现后期绑定。
COM使用上面IDispatch接口进行后期绑定。后期绑定可以使用调度接口和双重接口来实现。
在COM中,早起绑定有两个不同的选项。早起绑定的一种方式也称为虚拟表绑定,它直接使用虚拟表,这可以通过自定义接口和双重接口来实现。早期绑定的第二种方式也称为ID绑定。其中调度ID存储在客户端代码中,在运行期间只需要调用一次Invoke()方法。GetIdsOfNames()方法在设计期间调用。对于这种客户端,记住不必改变调度ID非常重要。
8.数据类型
对于双重接口和调度接口,COM能使用的数据类型局限于一个自动兼容的数据类型列表。IDispatch接口的Invoke()方法接受VARIANT数据类型的数组。VARIANT是许多不同数据类型的联合,如BYTE、SHORT、LONG、GLOAT、DOUBLE、BSTR、IUnknown*、IDispatch*等。
9.注册
.NET区分私有程序集,而在COM中,通过注册表配置的所有组件都是全局可用的。
所有COM对象都有一个唯一的标识符,该标识符由一个128位的数字组成,也称为类ID(CLSID)。创建COM对象时,COMAPL调用CoCreateInstance()方法,仅在注册表中查找CLSID和DLL或EXE的路径,然后加载DLL或启动EXE,并实例化组件。
因为这个128位的数字不容易记忆,所以想多COM对象还有一个OrigID,该ID很容易记忆,如Excel.APPlication就映射到一个CLSID上。
除了CLSID之外,COM对象还为每个接口和类型库指定一个唯一的标识符(分别为IID和typelib Id)。
10.线程
COM使用单元模型,这样程序员就不必考虑线程问题。但是,这也增加了复杂性。对于不同的操作系统版本添加了不同的单元类型。
单线程单元
单线程单元(STA)中,只允许一个线程(创建实例的线程)访问组件.但是,在一个进程中允许有许多个STA,在STA中,不需要防止多个线程访问实例变量,因为这种保护由COM设备实现,只有一个线程可以访问组件。
COM对象在编程时不是线程安全的,因此STA需要在注册表中吧注册键ThreadingModel设置为Apartment。
多线程单元
COM对象在编程时是线程安全的,因此MTA需要在注册表中吧ThreadingModel键设置为Free。Both值用于不考虑单元类型的线程安全的COM队象。
11.错误处理
在.NET中,通过抛出异常来生成错误。在较旧的COM技术中,通过方法返回HRESULT值来定义错误。HRESULT的值是S_OK,它表示方法成功。
如果COM组件提供了详细的错误信息,COM组件就实现ISupportErrorInfo接口,该接口不但提供错误消息,还提供了帮助文件的链接、错误源,一返回方法就会返回一个错误信息对象。在.NET中,实现ISupportErrorInfo接口的对象会自动映射到详细的错误信息和一个.NET异常。
12.事件
.NET用C#关键字event和delegate提供了回调机制。
在COM事件中,组件必须实现IconnectionPointContainer接口和一个或多个实现IConnectionPoint接口的连接点对象(CPO)。组件还定义一个由CPO调用的输出接口ICompletedEvent。客户端必须在sink对象中实现这个输出接口,而sink对象本身是一个COM对象。在运行过程中,客户端在服务器中查询IConnectionPointContainer接口。通过这个接口,客户端让CPO通过FindConnectionPoint()方法,获得指向sink对象的指针传递给服务器。接着,组件就可以在客户端的sink对象中调用方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值