将dotNET组件暴露给COM的方法
将dotNET组件暴露给COM的方法
将 dotNET组件暴露给COM
小气的神
2002-4-23
Article Type: In-Depth
难度等级:6/9
版本:2.32
AutoDispatch参数产生的类型库描述: coclass dotNETClass { [default] interface _dotNETClass; interface _Object; };
[ odl, uuid(BCE3DFCD-F8F6-32F6-BB38-6F4928090805), hidden, dual, oleautomation, custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, COMVisible.dotNETClass)
] interface _dotNETClass : IDispatch { }; None参数产生的类型库描述: coclass dotNETClass { [default] interface _Object; }; |
如果dotNET组件的客户是一个VB或需要双接口的Script型环境,那么我们最好的选择是: AutoDual.
否则我们就可能在VB下就真的会发生一开始讲的那些问题。
至于第6点是有关注册的,如果你没有在VS.NET中设置自动注册,那么这个dotNET如果要暴露给COM,那么需要你手工注册。你可以在VS.NET中点击Solution Explorer中项目,然后右键中的属性,在配置页打开这个开关。如下图
我建议你完成所有的编码在第一次测试前打开这个开关。这个开关基本上和你的两个手工操作的功能相同,一个是regasm 组件.dll 和 tlbexp 组件名 /out:Tlb文件名。如果自动方式VS.NET会自动UnRegasm和进行再注册,同时我们3-4步保证它不会编译一次产生一个新的GUID号而让你迷惑和担心搞乱你的注册表。
不过如果我们可以改变我们原来的习惯的话,我们应该更多的使用Interface,而不是像原来一样去直接在VB中创建一个Class或组件。这样的方式可能更加优雅和健壮一些:
[GuidAttribute("ATC869A7-F101-4e7c-B062-CE56FDA5F678")] public interface : IdotNETClass { public int Add( int x, int y ) ; public int AddEx(intx , int y , int z ) ; public string Hello() ; }
[GuidAttribute("08C869A7-F101-4e7c-B062-CE56FDA5F352")] public class dotNETClass : IdotNETClass { ................. } |
精确定义类所遵循的接口是让你的同伴感到尊敬的一种设计和代码习惯,这样类倒出类型库时,所有的工具不必猜测类的定义。对于VS.NET这类有一些感知功能的IDE,不用太多的属性它都可以理解你的意图了。
下面是一些常用的COM互操作的支持属性,这些属性是CLR将”Managed”类作为COM对象,暴露给非托管领域所必须的。这些信息大多在System.Runtime.Interopservices中可以找到。
属性 |
例子 |
适应范围 |
说明 |
ComVisible |
[ComVisible(false)] |
类,结构,接口, 委托,配件 |
决定生成的类型库中不需要暴露的CLR公共类型 |
DispID |
[DispID(3)] |
方法,属性 |
确定DispID,供IDispatch接口访问 |
ProgId |
[ProgId(“dotNet.C”] |
类 |
类所用的ProgID |
Guid |
[GuidAttribute(“xxx”)] |
类,接口、模块 结构,枚举委托 |
指定COM GUID。写法上要小心和Guid类型冲突 |
InterfaceType |
[InterfaceType(ComInterfaceType.InterfaceIsDual)] |
接口 |
指定类型库生成任何一种COM的接口类型,比如:IUnknown |
ComRegisterFunction |
[ComUnregisterFunction()] |
方法 |
请求Regasm.exe执行一个方法 |
HasDefaultInterface |
MSDN Help |
类 |
指定默认接口 |
NoIDispatch |
MSDN Help |
类 |
指示从IUnknown派生 |
MarshalAs |
MarshalAs() |
属性 |
定义列集 |
我不完全认为“dotNET可以完全取代以前COM的编程方式,但无法取代COM+”这样的结论一定正确或这样的时代已经来临,但的确目前dotNET提供的COM互操作的特性足够维持一个很好的兼容和互操作程度;如果你以前使用VB,VC++进行COM的开发,那么现在你仍可以在dotNET平台上做同样的事情,有些微妙变化的是,在经历某个实践的曲线之后,你会发现dotNET平台下更方便和节省时间,当然你必须保证自己不是很介意性能,因为这种交互不可避免的需要以一些性能为代码。
好了最后我会举这样一个例子:在dotNET之前我们针对Biztalk Server的开发多是使用COM的方式,自从Biztalk Server 2002 Toolkit for Microsoft.NET的发布之后,你可以用dotNET做更多的一些工作。比如其中我们经常会编程开发的Application Integration Component(AIC),这些组件除了要实现一定接口之外,还必须要求我们登记到注册表的某个特定的Catalog ID下,用VC++的ATL可以用BEGIN_CATEGORY_MAP 宏,但是其他的方式几乎都需要手动生产和完成注册步骤,Microost最后索性告诉你,对于AIC组件最简单的办法是把它们安装到COM+中,这样不仅不用注册而且还能提高性能。但如果我们知道上面的属性使用ComRegisterFunction/ComUnregisterFunction,就可以告诉Regasm.exe在注册时执行某个我们定义的方法来完成特定的注册,最大的发挥组件自描述的功能。在附带的例子中我们可以看到当我们在原来类实现的最后加上这样的代码:
[ComRegisterFunction()] public static void RegisterFunction(Type t) { try { // Implements CATID_BIZTALK_COMPONENT category Registry.ClassesRoot.CreateSubKey("CLSID//{" + t.GUID.ToString().ToUpper() + "}//Implemented Categories//{5C6C30E7-C66D-40E3-889D-08C5C3099E52}"); // Implements CATID_BIZTALK_CUSTOM_PROCESS category Registry.ClassesRoot.CreateSubKey("CLSID//{" + t.GUID.ToString().ToUpper() + "}//Implemented Categories//{BD193E1D-D7DC-4B7C-B9D2-92AE0344C836}");
} catch(Exception e ) { throw new Exception( e.Message,e ); } } |
Regasm在注册时就会执行这段代码完成对特殊的Catalog的条目注册。
类似的情况还会很多比如SQL Server,Visio,Office 插件,Smart tags甚至AutoCAD for win等等许许多多支持COM接口的著名应用开发程序,你可以应用dotNET的互操作性来完成以前COM环境下的工作,并且工作在dotNET环境下,你可以从整个dotNET框架中获得一些以前平台所没有的好处,比如:命名空间、组件强名和多版本、密匙、自描述组件、线程模式等等,生产率可能是让你感到最愉悦的:) 当你跨越了为什么要使用dotNET组件而将焦点放在如何将dotNET组件暴露给COM以及有效的做到这一点时,我想,这种思考和实践本身会给你带来许多不同的体验和好处。
以上我们简单的考察了dotNET和互操作的一些最基本和常用的方法,在使用dotNET和向dotNET迁移的过程中我们会发现,对于dotNET调用COM组件或是COM应用调用dotNET组件的情况会逐渐减少,但可能很长一段时间还都会存在,越来越多的情况是dotNET对dotNET的“Managed”环境,互操作只是为了最好的兼容性,不过如果你十分喜欢和难以忘怀诸如晚绑定这样的迷人特性,那么充分使用这种互操作将是必须的。好的互操作使得我们可以有更多的选择,也可以保留我们喜欢的特性,一半COM一半dotNET的架构,保证平滑的迁移。但象上面所述的那样,或许某种情境下互操作可能变成你唯一的选择或是一条必经之路:)
相关文件下载: [COMVisible.zip 29K ]
本文原创,CSDN