65.9K
CodeProject 正在变化。 阅读更多。
Home

ActiveX EXE 包装器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.65/5 (13投票s)

2006年11月22日

CPOL

9分钟阅读

viewsIcon

113333

downloadIcon

1341

如何将 .NET EXE 程序集暴露给 COM 兼容的客户端应用程序(例如 VB6 或 VBScript),并强制客户端应用程序使用 .NET EXE 程序集的正在运行的实例。

问题

当我创建一个新应用程序时,我会在 .NET 中创建它。但是,我确实有许多仍在生产中的遗留应用程序,它们是用基于 COM 的语言(例如 VB6)编写的。为了使情况更加复杂,许多基于 COM 的应用程序被编写为 ActiveX EXE 服务器。我有多个 ActiveX EXE 服务器通过它们公开的 COM 接口相互通信。我的目标是将这些应用程序迁移到 .NET 框架。

借助 .NET COM 互操作性,可以非常轻松地将方法和属性暴露给任何 COM 兼容的语言,例如 VBScript 或 Visual Basic 6。事实上,对于 VB.NET 应用程序和 Visual Studio 2005,这变得微不足道:只需向 VB.NET 项目添加一个 COM 类即可完成。

C# 需要更多的工作才能将程序集暴露给 COM——您需要创建自己的 GUID 并实现自己的接口(您还需要使用 REGASM.exe 来创建类型库)。.NET 类库(DLL)和 Windows 应用程序(EXE)都可以暴露给 COM。本文不详细解释如何将方法和属性从 .NET 程序集暴露给 COM。CodeProject 上有很多优秀的文章详细解释了此过程。

当通过公开的 COM 接口调用 .NET 程序集时,会出现一个问题。通过 COM 接口调用程序集很容易(例如,在 VBScript 中使用 CreateObject(...) 或在 VB6 中使用“New”关键字),但是,您会很快注意到存在问题

  • COM 应用程序不使用 .NET 程序集的正在运行的实例。

此外,COM 应用程序在其相同的地址空间中加载 .NET 程序集。对于 VBScript,每次执行调用公开的 COM 接口的脚本时,都会加载 .NET 程序集的新实例。脚本结束后,.NET 程序集也随之结束。

问题摘要

我们如何将 .NET EXE 程序集暴露给 COM 兼容的客户端应用程序(例如 VB6 或 VBScript),并强制客户端应用程序使用 .NET EXE 程序集的正在运行的实例?如果 .NET EXE 程序集没有正在运行的实例,则客户端应用程序应调用 .NET 程序集的新实例。

本质上,我们要求 .NET EXE 程序集像 ActiveX EXE 服务器(进程外服务器)一样运行,我们可以从任何 COM 兼容语言进行延迟/早期绑定。

背景

您可能会问自己,“为什么要让 .NET 程序集模仿 ActiveX EXE 服务器的行为?”答案是:强制 .NET EXE 程序集模仿 ActiveX EXE 服务器的行为对于分阶段升级遗留 ActiveX EXE 服务器应用程序到 .NET 非常有用。我认为所有 VB6 Windows 应用程序都应该升级到 .NET。然而,我们中的许多人无法一次性将每个遗留应用程序升级到 .NET;升级必须分阶段进行。此外,您的一些遗留应用程序可能被编写为暴露给脚本语言(VBScript 或 JavaScript)。我们如何继续执行这些脚本并使其与新的/升级的 .NET 框架程序集兼容?

为了强调我的观点,请考虑以下示例:应用程序 A 和应用程序 B 是安装在数千台计算机上的企业应用程序。每个应用程序都是作为 VB6 ActiveX EXE 服务器应用程序编写的,每个应用程序都通过其公开的 COM 接口与其他应用程序“通信”。应用程序 B 将首先升级到 .NET(并投入生产),然后升级应用程序 A。在升级过程中,应用程序 A 和应用程序 B 之间的通信不能中断。

解决方案

再次,我们面临着将遗留 VB6 ActiveX EXE 服务器应用程序升级到 .NET 并避免破坏暴露给其他遗留 VB6 ActiveX EXE 服务器应用程序的通信通道的问题。有许多解决方案可以用来解决此问题。一种可能的解决方案包括在 .NET 程序集中使用 TCP/IP 侦听器来监听客户端应用程序传入请求的端口。VB6 客户端应用程序可以使用 WinSockets 与 .NET 程序集“通信”。但是,此解决方案将需要更改所有需要与升级的 .NET EXE 程序集通信的遗留 VB6 应用程序(更不用说重新测试和重新部署遗留应用程序到数千台计算机了)。

在本文中,我将探讨在 .NET EXE 程序集周围创建 ActiveX EXE 包装器。在新的 .NET EXE 程序集周围使用包装器的优点是,其他遗留 VB6 ActiveX EXE 应用程序无需修改(如果其他 VB6 应用程序不使用后期绑定,您必须确保包装器中的 COM 接口与被替换的遗留 ActiveX EXE 相同)。

ActiveX EXE 包装器将负责:

  1. 启动 .NET EXE 程序集。.NET EXE 程序集将在与 ActiveX EXE 包装器相同的地址空间中加载。
  2. ActiveX EXE 包装器将通过程序集中公开的 COM 接口与 .NET EXE 程序集通信。
  3. ActiveX 包装器还将具有其他 COM 应用程序可以调用的公开属性和方法。

本质上,ActiveX EXE 包装器会将收到的 COM 请求“冒泡”到 .NET EXE 程序集。

步骤 1:创建公开 COM 方法的 VB.NET 应用程序

为了说明 ActiveX EXE 包装器的概念,这里有一个简单的示例。VB.NET Windows 应用程序称为“Message Net”,其界面如下所示:

这是一个非常简单的应用程序,它接收字符串消息并在列表框中显示它们。该应用程序由一个窗体(frmMain)和两个类(AppComExpose)组成。

App 类仅持有对 frmMain 的静态/共享引用。

Public Class App

    Public Shared MainForm As frmMain

End Class

frmMain 类显然包含创建用户界面的代码。此外,frmMain 包含以下代码:

Private Sub frmMain_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load

    ' save a reference of this object in the App shared variable
    '   (if it's not already there!)
    If App.MainForm Is Nothing Then App.MainForm = Me

End Sub

以及按钮点击代码:

Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click

ListBox1.Items.Insert(0, DateTime.Now.ToLongTimeString() & _
         " - Hello World!  This was added internally")

' Alternativly, we could use the ComExpose object
' for adding items directly to the list box

'Dim MyComExpose As ComExpose

'MyComExpose = New ComExpose()
'MyComExpose.DisplayMessage("Hello World!  This was " & _ 
'       "added internally through the ComExpose object")

 End Sub

就是这样;这是一个完整的(尽管不太有用)应用程序。接下来,我们需要通过 COM 接口公开列表框。如前所述,Visual Studio 2005 中的 VB.NET 项目允许您添加“COM 类”(在解决方案资源管理器中,右键单击项目,“添加,新建项……”,然后选择“COM 类”。一个空的“COM 类”将被添加到您的项目中。

<ComClass(ComClass1.ClassId, ComClass1.InterfaceId, ComClass1.EventsId)> _
Public Class ComClass1

#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = _
           "3d9e2e6f-5bf2-4144-8790-4e5b0af104e9"
    Public Const InterfaceId As String = _
           "b89d94d5-2306-4afc-a3b5-e86a1958fb9d"
    Public Const EventsId As String = _
           "1e3a740b-f7a9-4d31-9a7d-db11210f0c01"
#End Region

    ' A creatable COM class must have a Public Sub New() 
    ' with no parameters, otherwise, the class will not be 
    ' registered in the COM registry and cannot be created 
    ' via CreateObject.
    Public Sub New()
        MyBase.New()
    End Sub

End Class

添加到此类的任何公共方法和属性都将被公开给 COM 客户端。向这个新类添加 StartApplication()DisplayMessages() 方法:

Public Sub StartApplication()

'**********************************************
' THIS METHOD MUST BE CALLED BY AN OUT
' OF PROCESS COM SERVER to start the .Net               
' application!!!!   
'**********************************************

    ' create the new form
    Dim frm As frmMain
    frm = New frmMain()

    ' save the reference to the form in a static variable
    App.MainForm = frm

    ' display some initial text in the list box
    frm.ListBox1.Items.Add(".Net application starting")

    ' show the form – needs to be shown modally.
    frm.ShowDialog()

End Sub

   
Public Sub DisplayMessage(ByVal Message As String)

'**************************************************
' this method displays the message from
' the parameter in the list box in 
' frmMain.    
'**************************************************

    If Not App.MainForm Is Nothing Then
        App.MainForm.ListBox1.Items.Insert(0, _
DateTime.Now.ToLongTimeString() & " - " & Message)
    End If

End Sub

为了完成我们的应用程序并完全将其公开给 COM,还有一个最后的步骤:创建类型库。.NET 框架附带一个名为 REGASM.exe 的实用程序,允许您注册暴露给 COM 的程序集。REGASM 实用程序还允许您创建可以在许多编程 IDE(包括 VB6)中使用的类型库(TLB)。要使用 REGASM,只需运行 Create.Bat 文件(您可能需要在 bat 文件中更新路径)。请注意,MsgNet.tlb 已被创建。

现在我们有了一个公开 COM 方法的 .NET 应用程序,让我们用 VBScript 调用 .NET 程序集(脚本可以在文件 TestMsgNet.vbs 中找到)。

Dim MyMsgNet

Set MyMsgNet = CreateObject("MsgNet.ComExpose")
MyMsgNet.StartApplication
MyMsgNet.DisplayMessage ("Hello from VBScript")
Set MyMsgNet = Nothing

从测试中可以看出,它不起作用。主界面和初始消息“.NET application starting”已显示,但是,“Hello from VBScript”文本从未显示。还请注意,每次执行上述 VBScript 时,.NET 应用程序都会重新加载。

这给迁移应用程序到 .NET 带来了问题。您可以同时将所有 ActiveX EXE 服务器应用程序迁移到 .NET,并使用 .NET Remoting 作为各种 AppDomain 之间的通信工具,然而,这种方法不切实际。因此,ActiveX EXE 包装器分阶段方法。

步骤 2:将 VB.NET 项目包装在 ActiveX 服务器中

上述解决方案帮助不大。我们希望 .NET EXE 程序集像 ActiveX EXE 服务器一样运行。也就是说,我们希望所有客户端应用程序在可用时都利用正在运行的实例。为了实现这一目标,我们需要用 ActiveX 服务器包装 .NET 程序集。我知道这是一个额外的步骤,但它是一个必要的步骤(否则,您需要更改每个 ActiveX EXE 服务器应用程序,重新编译并部署以使用 TCP/IP,例如)。要完成此练习,您需要使用一种允许您创建 ActiveX EXE 服务器的编程语言。我将使用 VB6 来演示。

  1. 在 VB6 中创建一个新的 ActiveX EXE 项目。

  2. 添加对您使用 REGASM 实用程序创建的“MsgNet.tlb”类型库的引用。
  3. 在 VB6 应用程序中创建一个模块,并定义一个名为 MyMsgNet 的全局变量。该模块还应包含一个“Sub Main”过程来启动 ActiveX EXE 服务器。

    Option Explicit
    
    Public MyMsgNet As MsgNet.ComExpose
    
    Public Sub Main()
    
        Set MyMsgNet = New MsgNet.ComExpose
        MyMsgNet.StartApplication
                
    End Sub
    

    注意:您必须将 VB6 项目中的“Startup Object”设置为“Sub Main”。

    另请注意,您需要将“Start Mode”设置为“Standalone”。

  4. 最后,完成 .NET 程序集的 ActiveX EXE 包装器,您需要创建一个 VB6 类,该类将简单地调用 .NET 程序集中的相应方法。
  5. 在 VB6 项目中创建一个名为“ComExpose”的类,并添加以下代码:

    Option Explicit
    
    Public Sub DisplayMessage(ByVal Message As String)
    
        MyMsgNet.DisplayMessage Message
        
    End Sub

一切完成;ActiveX EXE COM 包装器已完成。编译应用程序。请注意,当您执行 VB6 ActiveX EXE 包装器时,.NET 应用程序将被调用。

另请注意:

  • 任务管理器显示两个应用程序都在运行:.NET EXE 程序集和 VB6 ActiveX EXE 包装器。

  • 如果 .NET EXE 程序集关闭,VB6 ActiveX EXE 包装器也将关闭。

步骤 3:测试 ActiveX EXE COM 包装器

之前,我们使用了 VBScript 来测试我们的 .NET EXE 程序集,但发现结果不佳。消息从未在 .NET 程序集中显示。要测试新的 VB6 ActiveX EXE 包装器,我们只需要对 VBScript 进行一个小小的修改:

Dim MyMsgNet

Set MyMsgNet = CreateObject("MsgNet_Com.ComExpose")
MyMsgNet.DisplayMessage ("Hello from VBScript")
Set MyMsgNet = Nothing

我们将 CreateObject(“MsgNet.ComExpose”) 更改为 CreateObject(“MsgNet_Com.ComExpose”),以便从 .NET EXE 程序集中的 COM 接口切换到 ActiveX EXE 包装器。

请注意,当您现在执行 VBScript 时,它将利用 .NET EXE 程序集的正在运行的实例。目标已经实现:.NET EXE 程序集“表现得”像一个 ActiveX EXE 服务器。

结论

使用 ActiveX EXE 包装器,您可以使 .NET 程序集表现得像 ActiveX EXE 服务器。这对于分阶段升级遗留 VB6 应用程序到 .NET 或允许使用 VBScript 扩展 .NET 程序集非常有用。

您可以通过 donaldsnowdy@hotmail.com 联系到我。

ActiveX EXE 包装器 - CodeProject - 代码之家
© . All rights reserved.