Com 与 .Net 互操作
一、 .Net 调用 Com 组件
.Net 调用 Com 组件主要分为两类:静态调用及动态调用。
所谓静态调用:指通过通过 tlbimp.exe 命名产生 Com 组件在 .Net 环境下的包装类,然后通过这个包装类来访问 Com 组件。
所谓动态调用:是指不通过 Com 组件的包装类来进行 Com 组件调用,而是在远行时通过反射来进行 Com 组件调用。
下面将分别详细的讨论这两种实现方法:
² 静态调用
1、 编写 Com 组件 MyComponent.dll
2、 产生可访问 Com 组件的包装类:
tlbimp /out:Interop.MyComponent.dll MyComponent.dll
3、 在 .Net 代码中访问
只要在项目添加 Com 包装类,就可以像访问 .Net 的装配件一样访问 Com 组件。
当然上面的三个步骤也可以在 VS 中直接添加对 Com 组件的引用,让 VS 自动完成第二步。
** 注意: **
1、 如果引用 Com 组件的 .Net 项目为结果为强命名组件,这个时候要求 Com 组件的包装类也必须为强命名组件,所以这个时候必须手动进行第二步操作,并且在产生包装类的时候设置 snk 文件名称,具体操作方法为:
tblimp /keyfile:filename.snk /out:Interop.MyComponent.dll MyComponent.dll
2、 要想有更多的设置请参考 tlbimp 的使用方法。
² 动态调用
1、 编写 Com 组件 MyComponent.dll 。
2、 在 .Net 程序中产生要被调用的 Com 组件类的 Type :
Type comType = Type.GetTypeFromCLSID( Guid ); 或
Type comType = Type.GetTypeFromProgID( string );
3、 生成 Com 组件类对象
object comObj = Activator.CreateInstance( comType ); 或
object comObj = comType.InvokeMember(
null,
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.CreateInstance,
null, null, args );
4、 设置参数及其对应的 ByRef 属性。
object[] args = new object[]{arg1, arg2 , …,argn};
ParameterModifier[] modifiers = new ParameterModifier[1];
modifiers[0] = new ParameterModifier( argNumCount );
// 设置参数是否为 ByRef
modifiers[0][0] = true;// 表示该参数是 ByRef ( InOut/Out )
modifiers[0][n] = false;// 表示该参数是 ByValue ( In )。
5、 调用 Com 组件方法或者属性。
object returnValue = comType.InvokeMember(
“MethodName” ,
BindingFlags.InvokeMethod|… ,
null ,
comObj ,
args ,
modifiers ,
null );
** 注意: **
1、 调用 Com 组件方法的时候,不能够采用 MethodInfo 的方式进行调用,由于没有 Com 组件的源数据信息,不能通过 Type.GetMethod 等方法够获取与 Com 组件相关的任何特有类型信息。
2、 动态调用 Com 组件方法中,如果需要参数回传值,则必须将该参数对应的 ParameterModifier 表示设置为 True ,即使该参数类型为传址类型。 (在 .net 中如果参数为传址 类类型,那么参数默认行为是ByRef的)。
3、 如果传递给 Com 组件的参数设置为传引用的( ParameterModifier[n] = true )的时候,该参数的值不能够为空( Null) ,否则会产生一个 TargetInvocationException 异常。 解决的方法为,如果参数为基本类型(传值)则产生一个默认的值,如果参数为非基本类型(传址)则使用 new UnknownWrapper(null) 进行替代该参数。
二、 Com 调用 .Net 组件
具体步骤演示如下:
1、 定义接口:
Com 是通过抛出接口让外部应用调用的,每个接口、组件都有一个 Guid ,在 .Net 中开发 Com 组件也不例外。
[Guid(“0bb13346-7e9d-4aba-9ff2-862e 0105489a ”)]
public interface IMyService
{
// 定义接口方法或者属性
// 每个方法都有一个 DISPID 属性,供 VBScript 等使用
[DispId( 1 )]
void Method1( args … );
}
2、 实现接口的派生类
//guid 属性定义组件的 Guid
//ProgID 定义 Com 组件的 ProgID
[Guid( "ba 0a 3019-f0d8-4406-8116-f80d 5515c 686" ) ,
ProgId("ClassNamespace.MyService"),
ClassInterface( ClassInterfaceType.None)]
public class MyService : IMyService
{
…… // 实现代码部分
}
3、 将 .Net 装配件转换为公有装配件。
(1) 、创建强名字
sn –k MyService.snk
并将强名字拷贝到工程目录下面
在 AssembyInfo.cs 修改对应的配置
[assembly: AssemblyKeyFile(@“../../MyService.snk”)]
(2) 、注册装配件
RegAsm /codebase MyService.dll
** 注意 ** : /codebase 是指设置注册表中的基本代码,也就是记录下 dll 的路径,供 ccw 在 Com 客户端在任何位置都可以找到该 dll ,如果没有 /codebase 属性,则 ccw 默认为 dll 与 Com 客户端执行程序的当前目录下面。
( 3 )、将装配件加入 GAC
gacutil -i MyService.dll
** 注意: ** 该步骤不是必须的。
( 4 )、导出类型库
为了在 VB 中使用,必须使用 tlbexp.exe 导出 Com 类型库。
tlbexp /out:MyService.tlb MyService.dll
经过上面的工作,就将一个 .NET 装配件转化为 COM 组件。
** ** ** 注意: **
** ** 在 Com 中调用的 .Net 对象必须具有下面的特性:
l 类必须是 public 性质 ;
l 特性、方法和事件必须是 public 性质的 ;
l 特性和方法必须在类接口中定义 ;
l 事件必须在事件接口中定义。