** 将 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