除了提供后向兼容之外,.NET框架还同样支持前向兼容性,其含义就是暴露.NET装配供COM利用是可能的。现在就让我们看看如何让COM利用.NET装配。
从COM调用.NET装配
现在让我们看看从COM利用.NET装配的原理,如图A所示。
图A
从COM利用.NET装配
和.NET调用COM对象的过程类似,这里我们对.NET装配采用了一个封装类,你可以通过COM应用程序对其进行访问。这种封装类就是所谓的COM可调用封装(CCW)。说了半天,我们该如何利用Visual Studio.NET达到以上目标呢?
首先启动Visual Studio.NET,然后创建Class Library类型的新项目,名称是DotnetForCOM(举个例子,你当然也可以起自己的名字)。给新类增加一个默认的构造器和一些公共方法,这些东西都是应该在COM中可用的。一旦做好以上准备即可右击Solution Explorer中的项目,选中Properties弹出项目属性页。然后在属性页上选择Configuration属性和Build选项。这样你的面前就会出现如图B所示的画面。
图B
使用属性页
选中Register For COM Interop (如图B所示)检查框。现在,当你构造项目之后.NET装配就会自动地注册COM Interop。这样你就能通过COM使用该装配了。比方说,用Visual Basic 6.0编程的时候,假如你启动一个新的Visual Basic 6项目并选择Project菜单和References,那么.NET装配就可以通过COM引用了(参看图C)。
图C
Visual Basic项目引用
到这里为止,你即可由Visual Basic 6访问.NET装配所提供的功能了。
Visual Studio.NET之外
下面我们讨论下不使用Visual Studio.NET时的情况。此时要让.NET装配暴露给COM具有额外的要求。
提供默认的构造器
首先,.NET装配必须提供默认的构造器。这是因为COM 客户不支持.NET参数化的构造器,所以你必须自己保证给类提供了默认的构造器。你也可以随默认构造器编写带参数的构造器,不过默认构造器需要从COM客户实例化.NET装配。
生成类型库
现在为.NET装配生成类型库,并在系统注册表中产生有关的条目。为此可以采用以下两种方式:
· 采用.NET Framework SDK 所提供的Type Library Exporter工具为.NET装配产生类型库。然后用Assembly Registration工具注册该类型库。
Tlbexp test.dll /out:test.tlb Regasm test.dll
·直接生成类型库并采用Regasm为其注册。
Regasm test.dll /tlb:test.tlb
.NET 装配必须有一个Strong Name并且驻留在全局装配缓存(Global Assembly Cache)里。为此你需要采用Strong Name工具(Sn.exe)为装配生成注册键值。完成这一操作之后即可利用GAC工具(Gacutil.exe)把装配添加到全局装配缓存。你还可以用以下语句把装配加到GAC:
gacutil –I SampleAssembly.dll
接下来你就可以通过COM给.NET装配增加引用并具体应用它了。CCW在由COM调用.NET装配的时候产生;它起到了受管和不受管领域之间的桥梁作用。
用属性修改汇集行为
属性(Attributes)可以用来修改汇集器(marshaler)所应用的汇集(marshaling)行为。这些属性分为3种类型,包括:
- 在设计时你应用的属性。
- 由Interop工具所应用的属性
- 由你或Interop应用的属性。
GuidAttribute、ProgldAttribute、MarshalAsAttribute和COMVisibleAttribute是最常用的属性。
GuidAttribute和ProgIdAttribute用来设定类的GUID和ProgId。
MarshalAsAttribute是可选的。因为所有的数据类型都有其默认的汇集行为。就数据类型而言,比如String类型,这类数据类型作为多种类型汇集是完全可能的,这一属性是非常必要的。.NET框架的String数据类型在不受管代码下意味着以下的类型:
- LPStr
- LPWStr
- LPTStr
- BStr
默认的行为是把String汇集为Bstr。这一属性可以用来给其他任何不受管类型修改以上行为。
COMVisibleAttribute用来控制装配之内的类型可见性。装配之内的公共类型默认是可见的。所以为隐藏某些类型起见就要用到属性了。
为了全面了解属性,请参看MSDN。
Interop的含义
另外还必须记住,从.NET调用COM对象的时候会影响系统的性能,反之也是一样的,原因就在于汇集。再有,这种性能上的影响与数据类型是无关的。像整数和字节这样的简单数据类型不会影响性能。原因是这类数据类型在受管和不受管内存中的表示都是一样的,所以Interop的工作任务也一样。汇集行为的影响达到了最小。String数据类型则会降低性能,因为其表示在受管和不受管内存中有差别。简单数据类型,也就是在受管和不受管代码中具有同样表示的数据类型,它们被称做blittable数据类型,而其他具有不统一表示的数据类型则被称做nonblittable数据类型。Interop Marshaler为所有的内建数据类型提供了支持。而对复杂的数据类型,你则需要手工编辑MSIL代码了。
采用经过测试的代码
.NET框架为现有的COM和新出现的.NET装配之间的互操作提供了相应的实现功能。这些功能是通过封装类的方式实现的,封装类就如同受管和不受管代码之间的桥梁。而开发人员则决定是否需要采用Interop以及在什么场合需要编写新的代码。我们的原则是,尽量采用经过测试的代码而不要编写需要测试的新代码。