从 COM 组件调用 .NET 组件

 

Mike Gunderloy
Lark Group, Inc.

2002 年 1 月

总结: 演练如何从 COM 客户端调用 Microsoft .NET 服务器的详细信息。 ) (12 个打印页

目标

  • 了解 COM 可调用包装器的概念
  • 创建可从 Microsoft® Visual Basic® 6.0 调用的 .NET 服务器
  • 使用 sn、regasm 和 gacutil 实用工具
  • 编写使用 .NET 类的 Visual Basic 6.0 代码

假设

以下内容应为 true,以便充分利用本文档:

  • 你熟悉 Visual Basic 编程
  • 你熟悉 COM 概念
  • 你有权访问 Visual Basic .NET
  • 你了解整体 .NET 体系结构
  • 了解如何在 Visual Basic .NET 中创建公共类

目录

互操作性的喜悦
创建用于 COM 应用程序的 .NET 类
练习从 COM 调用 .NET 组件
Visual Basic 6.0 以来的新增功能是什么?
总结

互操作性的喜悦

有时编程的一场革命迫使你放弃之前的所有操作。 举一个极端的例子,假设你已经编写 Visual Basic 应用程序多年了。 如果你和许多开发人员一样,你将在那个时候构建大量的代码清单。 如果你一直在遵循各种语言大师的建议,该代码将 组件化。 也就是说,通过使用 COM (组件对象模型) (以前为 Microsoft® ActiveX®)服务器,可将应用程序分解成多个可调用功能块。 当然,你也可能对其他开发人员和其他公司的组件(例如 ActiveX 控件)进行大量投资。

但是,如果你决定彻底将开发切换到另一个操作系统呢? 此时,你对 COM 的全部投资将变得毫无价值。 不能使用任何现有代码,必须了解如何在新平台上执行所有操作。 这无疑会对你的工作效率造成严重打击。

幸运的是,从 COM 切换到 .NET 不会造成这种根本的生产力损失。 有两个关键概念使从 COM 开发迁移到 .NET 开发要容易得多,而不会损失任何代码库或生产力:

  • .NET 组件可以调用 COM 组件。
  • COM 组件可以调用 .NET 组件。

这种双向互操作性是从 COM 迁移到 .NET 的关键。 了解 .NET 的复杂功能后,可以继续使用 COM 组件。 在很多情况下,这种互操作性非常有用:

  • 切换到 .NET 不会立即。 了解 .NET 编程概念和实现需要时间,因此你可能会发现,当你、同事和供应商快速上手时,你需要继续使用 COM 代码。
  • 无法一次性迁移所有可迁移到 .NET 的代码。 需要迁移,然后单独测试每个迁移的组件。
  • 你可能使用的是无法转换为 .NET 的第三方 COM 组件,并且供应商尚未发布 .NET 版本。
  • 尽管 Visual Basic 6.0 代码将迁移到 .NET,但迁移并不完美。 你可能有由于实现或语言怪癖而无法移动到 .NET 的组件。

本文档介绍从 COM 客户端调用 .NET 服务器的详细信息。 在本系列的另一篇文档 从 .NET 客户端调用 COM 组件中,你将了解如何从 .NET 客户端到 COM 服务器从另一个方向调用。

创建用于 COM 应用程序的 .NET 类

尽管 COM 客户端可以调用 .NET 服务器在公共类中公开的代码,但 COM 客户端无法直接访问 .NET 代码。 若要从 COM 客户端使用 .NET 代码,需要创建一个名为 COM 可调用包装器的代理 (CCW) 。 在本部分中,你将了解 CCW 体系结构,以及创建和部署 COM 客户端要使用的 .NET 类所需的步骤。

COM 可调用包装器

在 .NET 公共语言运行时 (CLR) 中运行 的代码称为托管代码。 此代码可以访问 CLR 为表带来的所有服务,例如跨语言集成、安全性和版本控制支持以及垃圾回收。 不在 CLR 中操作的代码称为 非托管代码。 由于 COM 是在 CLR 存在之前设计的,并且 COM 代码不在 CLR 提供的基础结构中运行,因此它无法使用任何 CLR 服务。 根据定义,所有 COM 组件都是非托管代码。

托管代码组件不仅依赖于 CLR,还要求与其交互的组件依赖于 CLR。 由于 COM 组件不在 CLR 中运行,因此它们无法直接调用托管代码组件。 非托管代码根本无法访问 CLR 来直接调用托管组件。

摆脱这种困境的方法是使用 代理。 一般来说,代理是一种软件,它接受来自组件的命令,对其进行修改,并将其转发到另一个组件。 从非托管代码调用托管代码时使用的特定代理类型称为 COM 可调用包装器(CCW)。 图 1 以示意图方式显示了 CCW 如何跨越托管代码和非托管代码之间的边界。 此图包括一个名为 ComUI.exe 的 COM 程序、两个名为 NETService.dll 和 Utility.dll 的 .NET 组件,以及连接它们的必要技术。

图 1. 使用 CCW 调用托管代码

COM 可调用类的先决条件

创建 COM 客户端将使用的 .NET 类时,需要记住两个先决条件。

首先,在 Visual Basic .NET 代码中显式定义接口,并让 类实现 接口。 例如,此代码片段定义名为 iFile 的接口和实现接口的类:

Public Interface iFile
    Property Length() As Integer
End Interface

Public Class TextFile
    Implements iFile
    ' details omitted
End Class

通过接口实现功能对 COM 客户端有一个主要好处。 生成 CCW 时,.NET 使接口与以前的版本保持一致。 这有助于防止对 .NET 服务器的更改中断 COM 客户端。

其次,任何对 COM 客户端可见的类都必须声明为公共类。 创建 CCW 的工具仅定义基于公共类的类型。 相同的规则适用于 COM 客户端将使用的方法、属性和事件。

还应考虑使用加密密钥对为包含 COM 使用的类的 .NET 程序集进行签名。 Microsoft 将其称为使用 强名称对程序集进行签名。 使用强名称对程序集进行签名有助于 .NET 确保程序集中的代码自程序集发布以来未发生更改。 这是所有 全局程序集的要求,即要由多个客户端共享的程序集,尽管 COM 客户端也可以调用未签名的程序集。

注意 通过将程序集作为专用程序集直接部署到 COM 客户端的目录,可以从 COM 客户端使用未签名 的程序集。 本文档不介绍专用程序集方法,因为全局程序集比与大多数 COM 应用程序的体系结构的专用程序集更兼容。

最好对所有程序集(甚至专用程序集)进行签名。 这将有助于为托管类生成更好的 CLSID,并有助于避免不同程序集中的类之间的冲突。

若要创建强名称,可以使用 sn 工具。 此命令行工具有许多选项,可以在命令提示符下键入 sn /? 来查看所有选项。 对程序集进行签名所需的选项是 -k,这将创建密钥文件。 默认情况下,密钥文件使用扩展名 .snk。 例如,若要创建名为 NETServer.snk 的密钥文件,可以使用以下命令行:

sn -k NETServer.snk

部署应用以实现 COM 访问

创建包含将由 COM 客户端调用的类的 .NET 程序集后,可通过三个步骤使类可供 COM 使用。

首先,必须为程序集创建类型库。 类型库是 .NET 程序集中包含的元数据的 COM 等效项。 类型库通常包含在扩展名为 .tlb 的文件中。 类型库包含必要的信息,使 COM 客户端能够确定哪些类位于特定服务器中,以及这些类支持的方法、属性和事件。 .NET Framework SDK 包含一个名为 tlbexp (类型库导出程序) 的工具,该工具可以从程序集创建类型库。 Tlbexp 包含许多选项,可以在命令提示符下键入 tlbexp /? 来查看所有选项。 其中一个选项是 /out 选项,用于指定生成的类型库的名称。 (如果不选择创建自己的名称,则创建 One。) 例如,若要将元数据从名为 NETServer.dll 的程序集提取到名为 NETServer.tlb 的类型库,可以使用以下命令行:

tlbexp NETServer.dll /out:NETServer.tlb

其次,应使用程序集注册工具 (从 .NET Framework SDK 重新) ,以创建类型库并在单个操作中注册它。 这是在单台计算机上同时进行 .NET 和 COM 开发时最容易使用的工具。 与 tlbexp 一样,有许多用于重新加气的选项:在命令提示符下键入 regasm /? 可查看所有它们。 若要使用 regasm 创建和注册类型库,请使用如下所示的命令行:

regasm /tlb:NETServer.tlb NETServer.dll

第三,必须将 .NET 程序集安装到全局程序集缓存 (GAC) ,以便它可用作共享程序集。 若要将程序集安装到 GAC 中,请使用 gacutil 工具:

gacutil /i NETServer.dll

同样,可以通过键入 gacutil /? 获取 gacutil 的所有选项的列表 。

练习从 COM 调用 .NET 组件

在以下示例中,你将通过 COM 代码在 .NET 组件中使用属性和方法。 你将使用 regasm 从 .NET 程序集创建类型库并注册程序集,并使用 gacutil 使程序集全局可用。 然后,你将了解如何在 Visual Basic 6.0 COM 代码中使用此 .NET 程序集。

创建 .NET 程序集

若要创建包含公共类的 .NET 程序集,请执行以下步骤:

  1. 打开 Microsoft® Visual Studio® .NET,然后单击起始页上的“ 新建项目 ”。

  2. 从屏幕左侧的树视图中选择 “Visual Basic 项目 ”。

  3. 选择“ 类库” 作为项目模板。

  4. 将应用程序的名称设置为 PhysServer2 ,然后单击 “确定” 创建项目。

  5. 突出显示解决方案资源管理器窗口中名为 Class1.vb 的类,并将其重命名为 NETTemperature.vb

  6. 在 NETTemperature.vb 中选择 Class1 的代码 (这将是一个空类定义) ,并将其替换为以下代码:

    Public Interface iTemperature
        Property Celsius() As Double
        Property Fahrenheit() As Double
        Function GetCelsius() As Double
        Function GetFahrenheit() As Double
    End Interface
    
    Public Class NET_Temperature
        Implements iTemperature
    
        Private mdblCelsius As Double
        Private mdblFahrenheit As Double
    
        Public Property Celsius() As Double _
         Implements iTemperature.Celsius
            Get
                Celsius = mdblCelsius
            End Get
            Set(ByVal Value As Double)
                mdblCelsius = Value
                mdblFahrenheit = ((Value * 9) / 5) + 32
            End Set
        End Property
    
        Public Property Fahrenheit() As Double _
         Implements iTemperature.Fahrenheit
            Get
                Fahrenheit = mdblFahrenheit
            End Get
            Set(ByVal Value As Double)
                mdblFahrenheit = Value
                mdblCelsius = ((Value - 32) * 5) / 9
            End Set
        End Property
    
        Public Function GetCelsius() As Double _
         Implements iTemperature.GetCelsius
            GetCelsius = mdblCelsius
        End Function
    
        Public Function GetFahrenheit() As Double _
         Implements iTemperature.GetFahrenheit
            GetFahrenheit = mdblFahrenheit
        End Function
    End Class
    

此代码首先定义名为 iTemperature 的接口。 由于接口是使用公共关键字 (keyword) 定义的,因此它将导出到将从此程序集创建的类型库。 可以将接口定义视为全部或部分类定义的主干。 接口定义可以包含成员 (属性、方法(函数或子)和事件) ,就像类一样。 但与类不同,接口定义不包含任何这些成员的代码。 一个类可以实现一个接口 (,如此示例中) 或多个接口。

此代码定义使用接口 iTemperature的 NET_Temperature 类。 具体而言,类定义中的这一行在 类和 接口之间设置协定:

Implements iTemperature

协定指出, 类将实现 接口的所有成员。 它还可能包含不属于接口的其他成员,但如果尝试生成未完全实现接口的类,则会收到错误。

注意 类公开两个公共属性和两个公共方法。 (有关创建类、方法和属性的基础知识,请参阅 在 Visual Basic .NET) 中创建类

请注意用于将 类中的成员与类实现的接口成员关联的语法。 例如,NET_Temperature 类中的摄氏度属性是这样定义的:

Public Property Celsius() As Double _
 Implements iTemperature.Celsius

该代码行定义返回 Double 的属性,并告知编译器此属性是 iTemperature 接口中摄氏度属性的实现。

创建密钥对并为程序集签名

若要使程序集全局可用,需要创建一个密钥对,并使用它来对程序集进行签名。 此外,还可以通过添加标题和说明来更轻松地使用程序集。

若要对程序集进行签名,可以运行 sn 实用工具并手动添加密钥文件的名称,也可以使用 Visual Studio .NET 用户界面生成强名称。 我们将使用后一种方法。 为此,请执行下列步骤:

  1. 在 Visual Studio .NET 的解决方案资源管理器中,双击 AssemblyInfo.vb 文件以在编辑窗口中将其打开。

  2. 在此文件顶部的“程序集属性”部分中,修改 AssemblyTitle 和 AssemblyDescription 行以读取:

    <Assembly: AssemblyTitle("PhysServer2")> 
    <Assembly: AssemblyDescription(".NET Version of PhysServer")> 
    

    **提示!   **Visual Basic .NET 中的程序集属性与 Visual Basic 6.0 中的项目属性等效。

  3. 在解决方案资源管理器中,右键单击项目节点,然后选择“属性”。 单击“ 通用属性” 文件夹,然后单击“ 强名称” 属性页。 选择标记为“ 使用生成强名称”的框。 单击“ 生成密钥 ”以生成密钥文件并将其添加到项目。 单击“ 确定” 关闭属性对话框。

现在已准备好创建程序集。 单击“ 生成 ”或按 Ctrl+Shift+B 生成程序集。

注册程序集并创建类型库

此时,可以使用另一个 .NET 应用程序中的新程序集和 NET_Temperature 类。 但是,你仍必须使类及其成员可供 COM 应用程序使用。 打开 Visual Studio .NET 命令提示符 (依次单击 “开始”、“ 程序”、“ Microsoft Visual Studio .NET 7.0”、“ Visual Studio .NET 工具”和 “Visual Studio .NET 命令提示符 ”) ,更改为 PhysServer2 的项目目录,然后键入:

regasm /tlb:PhysServer2.tlb PhysServer2.dll

regasm 实用工具将创建一个类型库并将其注册到 Windows 注册表中,使PhysServer2.dll中的类可供 COM 客户端使用。

将程序集添加到全局程序集缓存

最后,若要使新注册的程序集全局可用于所有 COM 客户端,无论它们位于硬盘驱动器上的何处,请切换回 Visual Studio .NET 命令提示符并键入:

gacutil /I PhysServer2.dll

gacutil 实用工具会将程序集添加到 GAC 并打印状态消息,告知你已完成该操作。

编写 Visual Basic 6.0 代码以调用 .NET 类

现在,你已准备好编写 COM 客户端以使用 NET_Temperature 类。 按照以下步骤操作:

  1. 打开 Visual Basic 6.0,在“新建项目”对话框中,单击“ 新建”选项卡

  2. 选择“ 标准 EXE ”,然后单击“ 打开”。

  3. 在“项目资源管理器”窗口中突出显示名为 Form1 的窗体,并将其重命名为 frmTemperature

  4. 通过添加相应的控件并设置这些控件的属性,创建图 2 所示的窗体,如表 1 中所述。

    表 1. frmTemperature 控件

    控件类型 属性
    Label 名称 lblFahrenheit
      Caption Fahrenheit
    TextBox 名称 txtFahrenheit
      文本 (空白)
    Button 名称 cmdConvertToC
      Caption 转换为 C
    Label 名称 lblCelsius
      Caption 摄氏温度
    TextBox 名称 txtCelsius
      文本 (空白)
    Button 名称 cmdConvertToF
      Caption 转换为 F

    图 2. 测试窗体设计

  5. 若要通过 CCW 使用 PhysServer2 中的 类,请单击“ 项目”,然后单击“ 引用 ”打开“引用”对话框。 选择 PhysServer 的 .NET 版本的引用,如图 3 所示。 单击 “确定” 关闭对话框。

    图 3. 设置对 .NET 组件的引用

现在,你已准备好编写使用 NET_Temperature 类的方法和属性的代码。 在“ 视图 ”菜单上,单击“ 代码”,然后在 frmTemperature 的窗体模块中输入此代码:

Private moTempClass As PhysServer2.NET_Temperature
Private moTemp As PhysServer2.iTemperature

Private Sub cmdConvertToC_Click()
    With moTemp
        .Fahrenheit = txtFahrenheit.Text
         txtCelsius.Text = .GetCelsius
    End With
End Sub

Private Sub cmdConvertToF_Click()
    With moTemp
        .Celsius = txtCelsius.Text
        txtFahrenheit.Text = .GetFahrenheit
    End With
End Sub

Private Sub Form_Load()
    Set moTempClass = New PhysServer2.NET_Temperature
    Set moTemp = moTempClass
End Sub

请记住,在 .NET 项目中,使用 iTemperature 接口定义了 Net_Temperature 类。 此代码演示如何检索接口 (表示为 名为 moTemp 的对象,) 从 对象返回。 尽管这可能看起来像是额外的代码,但如果在 Visual Basic 中试验,你会发现使用 接口比使用 对象更方便。 这是因为接口支持 Microsoft® IntelliSense® 命令完成。

试用

若要查看 NET_Temperature 类的运行情况,请执行以下步骤:

  1. F5 启动项目。
  2. 在“华氏度”文本框中输入 95 ,然后单击“ 转换为 C”。“摄氏度”框应填充值 35。
  3. 在“摄氏度”框中输入 -14 ,然后单击“ 转换为 F”。华氏度框应填充值 6.8。
  4. 关闭窗体以停止项目。

Visual Basic 6.0 以来有哪些新增功能?

当然,从 Visual Basic 6.0 调用 .NET 组件的整个过程都是新的,因为 .NET 在 Visual Basic 6.0 发布时不存在。 Visual Basic 6.0 确实能够创建多个通过 COM 调用连接的组件,而 .NET CCW 使 .NET 调用的功能类似于 COM 调用。 整个过程的利好之处在于,无需冒 .NET 组件的安全性和稳定性风险,就可以从 COM 代码使用它们。 通过 CLR 调用,无论在何处使用 .NET 组件,CCW 仍为 .NET 组件提供托管代码的所有优势。

总结

尽管 .NET 是一个全新的开发环境,但设计器并没有忽略与现有代码的兼容性问题。 通过正确构造 .NET 组件并使用 sn、tlbexp、regasm 和 gacutil 等工具,可以将 .NET 程序集中的类公开给 COM 客户端。

从 COM 组件调用 .NET 组件并不是一项简单练习。 如本文档所示,需要对 .NET 组件进行显式代码修改才能启用此方案。 但修改是次要的,启用 COM 客户端调用 .NET 服务器当然有好处。 如果要一次一个组件将复杂的应用程序从 COM 迁移到 .NET,你会发现本文档中概述的技术至关重要。

关于作者

迈克·金德洛伊 在华盛顿州东部写软件并饲养鸡。 他是 Access 2002 开发人员手册的合著者,也是 Sybex 的SQL Server Developer OLAP 与 Analysis Services 指南的作者。 自史前 Windows 时代以来,他一直在为 Microsoft 产品编写代码,无意很快停止任何时间。

关于 Informant Communications 组

Informant Communications Group, Inc. (www.informant.com) 是一家专注于信息技术行业的多元化媒体公司。 ICG 成立于 1990 年,专门从事软件开发出版物、会议、目录发布和网站。 ICG 在美国和英国设有办事处,是受人尊敬的媒体和营销内容集成商,满足了 IT 专业人员对优质技术信息的需求。

版权所有 © 2002 Informant Communications Group and Microsoft Corporation

技术编辑:PDSA, Inc. 和 KNG Consulting