您的位置:首页 > 其它

在COM应用程序中使用.NET组件

2007-05-15 22:20 513 查看
在.NET Framework中使用.NET组件很简单,只须要实例化组件然后运行即可,所有的信息通道都已被处理。为了在COM应用程序中使用.NET组件,CCW(COM Callable Wrapper)也提供了信息通道。
一 CCW(COM Callable Wrapper)
无论何时COM客户调用.NET对象时,CCW是一个动态创建的托管对象。该对象是访问.NET组件的有效代理。该代理将其自身作为COM对象向COM环境公开,允许模型透明。这样,COM应用程序中用于调用.NET对象的代码与用于调用任何标准COM对象的其他代码看起来很相像,对程序员隐藏了包装实现的细节。在COM应用程序中这2部分代码应该具有相同的功能可访问性。
对每个要访问的.NET组件,创建且只创建一个CCW。也就说,如果多个应用程序中的10个不同的COM组件访问同一.NET对象,则只创建了一个CCW。CCW要实例化的.NET对象分配在无用单元回收堆上,作为正常调用的.NET对象。但是,CCW本身分配在非收集托管堆上,使用COM组件的计数引用方法,允许COM组件(从固定位置)访问CCW。
一旦CCW的引用计数为0(不再有COM程序/组件拥有它的活动引用),就对.NET组件释放本身的引用,并且回收。现在,自由的.NET对象将在下次回收周期中被GC回收。
在COM应用程序中使用.NET组件时所涉及的基本结构如下图。



二 向COM环境公开的.NET组件编写限制
1.只有公共类型可见
COM二进制标准的限制是,没有办法区分公共、私有或内部公开类型。所以,通过包装向COM环境惟一能显式公开的类型是公共类型。
2.类需要默认构造函数
COM实际上并不支持构造函数概念。所以,当CCW代表COM客户端实例化.NET组件时,可调用的惟一构造函数是默认的、无参数的构造函数。
3.无静态成员
COM只能处理公开的接口。所以,CCW没有方法让客户端区分类和类实例的不同,所以不支持静态成员的声明。
4.方法重载被命名
方法重载在.NET中是个频繁使用的概念,但COM二进制标准不支持它的声明。所以,为了给COM客户端提供访问方法重载的方法,CCW增加了下划线和数字来生成惟一的方法签名。
三 向COM环境公开的.NET组件的编写步骤
1.定义接口和类;
2.在接口和类上使用ComVisibleAttribute和GuidAttribute;
3.类中加入两个注册表操作函数,以便添加和卸载额外的注册表信息。这几个注册表操作函数将会由
COM InterOp服务调用,因此需加上以下两个attribute:
ComRegisterFunctionAttribute
ComUnregisterFunctionAttribute
4.在.NET Component工程的AssemblyInfo.cs文件中加入[assembly: ComVisible(true)];
5.导出类型库并注册:
(1)通过VS.NET导出类型库并注册
确保您的类具有[ComVisible(true)]特性,然后打开Project | Properties配置页面,选中Register for COM InterOp复选框。这样,工程每次生成时,都将生成一个类型库和正确的注册表项。(注销时只需勾掉Register for COM InterOp复选框)
(2)手工导出类型库并注册
可以使用工具,TlbExp和RegAsm。
RegAsm:程序集注册工具读取程序集中的元数据,并将所需的项添加到注册表中。注册表允许 COM 客户程序以透明方式创建 .NET Framework 类。类一经注册,任何 COM 客户程序都可以使用它,就好像该类是一个 COM 类。类仅在安装程序集时注册一次。程序集中的类实例直到被实际注册时,才能从 COM 中创建。
regasm assemblyFile [options]

/codebase
在注册表中创建一个 Codebase 项。Codebase 项指定未安装到全局程序集缓存中的程序集的文件路径。如果随后要安装正在注册到全局程序集缓存中的程序集,则不应指定此选项。用 /codebase 选项指定的 assemblyFile 参数必须是具有强名称的程序集
/registered
指定此工具将仅引用已经注册的类型库。
/asmpath:directory
指定包含程序集引用的目录。必须和 /regfile 选项一起使用。
/nologo
取消显示 Microsoft 启动标题。
/regfile [:regFile]
为程序集生成指定的 .reg 文件,该文件包含所需的注册表项。指定此选项不更改注册表。此选项不能与 /u 选项或 /tlb 选项一起使用。
/silent/s
取消显示成功消息。
/tlb [:typeLibFile]
从指定的程序集生成类型库,该类型库包含在程序集中定义的可访问类型的定义。
/unregister/u
注销在 assemblyFile 中找到的可创建类。省略此选项将导致 Regasm.exe 注册程序集中的可创建类。
/verbose
指定详细模式;当用 /tlb 选项指定时,显示所有需要为其生成类型库的引用程序集的列表。
/?/help
显示该工具的命令语法和选项。
注意
Regasm.exe 命令行选项不区分大小写。只需提供足够的选项来唯一标识它。例如,/n 等效于 /nologo,而 /t:outfile.tlb 等效于 /tlb:outfile.tlb。
可以使用 /regfile 选项生成包含注册表项的 .reg 文件,而不是直接对注册表进行更改。通过注册表编辑器工具 (Regedit.exe) 导入 .reg 文件,可以在计算机上更新注册表。请注意,.reg 文件不包含任何可由用户定义的注册函数完成的注册表更新。注意,/regfile 选项只为托管类发出注册表项。此选项不为 TypeLibIDInterfaceID 发出注册表项。
指定 /tlb 选项时,Regasm.exe 生成并注册一个类型库,对在程序集中找到类型进行描述。Regasm.exe 将生成的类型库放到当前的工作目录中或为输出文件指定的目录中。为引用其他程序集的程序集生成类型库可能导致同时生成几个类型库。可使用类型库向开发工具(如 Visual Studio 2005)提供类型信息。如果正在注册的程序集是由类型库导入程序 (Tlbimp.exe) 产生的,则不应使用 /tlb 选项。如果程序集是从类型库导入的,则不能从它导出类型库。除了类型库导出程序 (Tlbexp.exe) 不能注册它产生的类型库外,使用 /tlb 选项同使用 Tlbexp.exe 和 Regasm.exe 的效果相同。如果使用 /tlb 选项注册某个类型库,则可将 /tlb 选项和 /unregister 选项一起使用,以注销该类型库。将两个选项一起使用将注销类型库和接口项,这样可较大程度地清理注册表。
当您注册一个程序集供 COM 使用时,Regasm.exe 会在本地计算机的注册表中添加一些项。更具体地说就是,它创建与版本相关的注册表项,这些项允许在一台计算机上并行运行同一程序集的多个版本。第一次注册程序集时,会为该程序集创建一个顶级项并为这个程序集版本创建一个唯一的子项。每次注册该程序集的新版本时,Regasm.exe 都为新版本创建一个子项。 ?????
例如,假设您要注册一个版本为 1.0.0.0 的托管组件 myComp.dll 供 COM 使用。后来,您又注册版本为 2.0.0.0 的 myComp.dll。您确定计算机上的所有 COM 客户端应用程序都要使用 2.0.0.0 版本的 myComp.dll,并且决定注销 myComponent.dll 版本 1.0.0.0。此注册表方案允许您注销 myComp.dll 版本 1.0.0.0,这是因为注销操作只移除 1.0.0.0 版本子项。



使用 Regasm.exe 注册程序集之后,可以将该程序集安装在全局程序集缓存中,以便可以从任何 COM 客户端激活它。如果程序集仅准备由单个应用程序激活,则可以将它放到该应用程序的目录中。
下面的命令注册 myTest.dll 中包含的所有公共类。
regasm myTest.dll
下面的命令生成文件 myTest.reg,该文件包含所有必要的注册表项。此命令不更新注册表。
regasm myTest.dll /regfile:myTest.reg
下面的命令注册 myTest.dll 中包含的所有公共类,并生成和注册类型库 myTest.tlb,该类型库包含 myTest.dll 中定义的所有公共类型的定义。
regasm myTest.dll /tlb:myTest.tlb
6.向COM环境公开的.NET组件实例
[align=left]using System;[/align]
[align=left]using System.Collections.Generic;[/align]
[align=left]using System.Text;[/align]
[align=left]using System.Runtime.InteropServices;[/align]
[align=left] [/align]
[align=left]namespace DotNetComponent[/align]
[align=left]{[/align]
[align=left] [ComVisible(true)][/align]
[align=left] [GuidAttribute("9DF923B7-3BC5-43a3-BB20-AE3A140FCF87")][/align]
[align=left] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)][/align]
[align=left] public interface IMath[/align]
[align=left] {[/align]
[align=left] double Add(double x, double y);[/align]
[align=left] }[/align]
[align=left] [ComVisible(true)] //指定该类型对COM可见[/align]
[align=left] [GuidAttribute("277C843A-E374-49d2-89F7-863D641F695A")][/align]
[align=left] public class DotNetComponetForCOM : IMath[/align]
[align=left] {[/align]
[align=left] public DotNetComponetForCOM(){}[/align]
[align=left] public double Add(double x, double y)[/align]
[align=left] {[/align]
[align=left] return x + y;[/align]
[align=left] }[/align]
[align=left] #region COM Register[/align]
[align=left] [ComRegisterFunctionAttribute()][/align]
[align=left] public static void Register(Type t)[/align]
[align=left] {[/align]

[align=left][/align]
}
[align=left] [/align]
[align=left] [ComUnregisterFunctionAttribute()][/align]
[align=left] public static void Unregister(Type t)[/align]
[align=left] {[/align]

[align=left][/align]
}
[align=left] private static string GetTypeGuid(Type t)[/align]
[align=left] {[/align]
[align=left] string guid = "";[/align]
[align=left] try[/align]
[align=left] {[/align]
[align=left] Object[] customAttributes = t.GetCustomAttributes(typeof(GuidAttribute), false);[/align]
[align=left] GuidAttribute guidAttribute = (GuidAttribute)customAttributes[0];[/align]
[align=left] guid = "{" + guidAttribute.Value.ToString() + "}";[/align]
[align=left] }[/align]
[align=left] catch[/align]
[align=left] {[/align]
[align=left] }[/align]
[align=left] return guid;[/align]
[align=left] }[/align]
[align=left] #endregion[/align]
[align=left] }[/align]
}
四 注册表中存储的信息
在下图的Registry Editor窗口中,可以看到注册表已登记的实际的DLL是mscoree.dll。存储的一些附加键值允许mscoree.dll来定位.NET程序集。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: