【问题标题】:Using WithEvents in vb6 with .Net (VB) dll在带有 .Net (VB) dll 的 vb6 中使用 WithEvents
【发布时间】:2018-09-03 07:50:30
【问题描述】:

我已经研究并阅读了我能找到的所有内容。我很可能已经看到了我的问题的解决方案,但没有认识到它。森林对我来说似乎太厚了。

我在 vb.net - VS2010 中编写了一个 dll。在 vb.net 项目中使用时,它可以完美运行。我已启用 COM 等以使其与 VB6 一起使用。

在我需要这些事件之前,它会按照 VB6 中的计划工作。但是现在试图让 WithEvents 部分工作是我完全迷失的地方!

一旦时间比较函数变为真(或相等),我的 VB.Net dll 就会引发一个事件。它还返回事件的实际时间。

在我的 VB6 项目中,使用 WithEvents 函数要求声明位于类模块中。我无法理解这将/应该如何工作。 正如我之前所说,我读过的许多帖子都指出它必须在一个类模块中,但没有一个确切地说明如何以一种干净直接的方法来完成这一点。好吧,至少在我看来是直截了当的。

以下代码是我在添加事件之前如何使用 dll。所有属性和操作等都可以正常工作。它位于 .bas 模块中。

Public PT As New PrecisionTimer.PrecisionTimer

这是如何在表单上使用它的示例:

Private Sub Command3_Click()
Dim aa As Integer

    aa = PT.SetTimerEvent(Val(Text6.Text))
    Select Case aa
        Case -1
            OkToInitiate = True
        Case False
            OkToInitiate = False
    End Select

End Sub

但现在我需要对 dll 中的事件做出反应。 这是引发事件的 VB.Net dll 中的行:

RaiseEvent OTE(ActualTriggeredTime.ToString())

那么,谁能帮助我,我真的很难弄清楚如何完成这个。

谢谢!

更多信息:
@TnTinMn,在某种程度上你是对的,CM 从来都不是我的强项。这是 MyClass 中的代码:

Option Explicit

Public WithEvents PTM As PrecisionTimer.PrecisionTimer

Private Sub PTM_OTE(ByVal ActualTriggeredTime As Variant)
Dim aa As Integer
   aa = 1
End Sub

现在可以删除PTM_OTE sub,自动出现一个新的,当然不用aa = 1代码。

在我的 Module1.bas 中:

Option Explicit
Public PT As New MyClass.PTM
Public OkToInitiate As Boolean

您在我的代码中看到错误了吗?

在对象浏览器中,我确实看到了 OTE 事件。在这一点上,我想我应该能够在左上角的代码编辑器下拉菜单中看到 PT,但我没有?!?!

更多信息:
我在 VB.Net dll 中的事件例程中添加了一个附加函数 - TriggeredFlag。

Public Sub OnTimedEvent(source As Object, e As ElapsedEventArgs)
   'Other code including basic error handling       
   RaiseEvent OTE(ActualTriggeredTime.ToString())
   TriggeredFlag = True
End Sub

现在当我在我的 vb6 项目中使用 dll 时,我可以监控 TriggeredFlag。它确实按预期发生了变化。然而,似乎没有收到事件。我相信问题出在 VB6 代码中 - 类模块中的 WithEvents 语句(可能)。这是我的整个班级模块:

Option Explicit

Public WithEvents PTM As PrecisionTimer.PrecisionTimer

Public Sub Class_Initialize()
    Set PTM = New PrecisionTimer.PrecisionTimer
End Sub

Private Sub PTM_OTE(ByVal ActualTriggeredTime As String)
Dim aa As Integer
    aa = 1
End Sub

最终附加信息:

感谢所有帮助解决此问题的人! 这是我的 Module1.Bas 的最终结果:

Option Explicit

Public PT As New PrecisionTimer.PrecisionTimer
Public DT As New DTParams

Public OkToInitiate As Boolean

在表格的常规部分:

Option Explicit

Public WithEvents TimerEvents As PrecisionTimer.PrecisionTimer

Form_load 例程包括:

Set TimerEvents = PT

这是 TimerEvents_OTE 例程:

Private Sub TimerEvents_OTE(ByVal ActualTriggeredTime As String)
'Fires when the "Timer Done" event fires

    Call SetTimerText(ActualTriggeredTime)

End Sub

最后,没有使用任何类模块。

再次感谢大家!

【问题讨论】:

  • 您在 VB6 中没有显式订阅事件,约定是给事件处理程序正确的名称。应该类似于带有这些 sn-ps 的 Sub PT_OTE(time As String),假设 PT 是声明 WithEvents 的变量,OTE 是事件的名称。否则与 Command3_Click 相同的想法,处理名为 Command3 的对象的 Click 事件。
  • @Hans,是的,你是对的 - PT 是用 WithEvents 声明的变量,而 OTE 是事件的名称。 1.我不清楚事件处理程序驻留在哪里。 2.intellesense不应该显示PT Sub吗?
  • 由于您没有向我们展示 VB.Net 代码以及如何将其暴露给 COM,我将引导您阅读文章:Walkthrough: Creating COM Objects with Visual Basic。这使它非常轻松。
  • @TnTinMn 是的,这是我最初使用的方法。我也刚刚重新验证了我的代码是这样的。
  • @TnTinMn,在某种程度上你是对的,CM 从来都不是我的强项,但是......这是 MyClass 中的代码: Option Explicit Public WithEvents PTM As PrecisionTimer.PrecisionTimer Private Sub PTM_OTE( ByVal ActualTriggeredTime As Variant) Dim aa As Integer aa = 1 End Sub

标签: vb.net vb6


【解决方案1】:

尝试使用 WithEvents 声明 PrecisionTimer:

Public WithEvents PT As New PrecisionTimer.PrecisionTimer

然后为 OTE 添加一个事件处理程序:

Public Sub PrecisionTimer_OTE(ByVal ActualTriggeredTime As String) Handles PT.OTE
...
End Sub

【讨论】:

  • 我假设 Public WithEvents 进入一个类模块。公共潜艇会去哪里?
  • 另外,VB6 似乎没有使用“Handles”功能。
  • @Ken 是正确的。 VB6 没有“句柄”关键字。
【解决方案2】:

WithEvents 不必在类模块中。可以在

  • 一个类模块
  • 用户控件
  • 一个表格

但它不能在模块中。

您的命令处理程序 (Command3_Click) 在表单中。您可以以相同的形式声明计时器。

Public WithEvents TimerEvents As New PrecisionTimer.PrecisionTimer

由于您在模块中将其命名为 PT,因此我将在表单中使用不同的名称,例如计时器事件。

声明此变量后,您可以在编辑器窗口左上角的列表中选择它...

然后在编辑器窗口右上角的列表中选择事件...

VB6 之后会自动插入事件处理程序,您可以在屏幕截图的底部看到。

在您的命令处理程序中,您必须在新变量中存储对计时器对象的引用

Set TimerEvents = PT

所以完整的功能应该是这样的

Private Sub Command3_Click()

    Dim aa As Integer

    Set TimerEvents = PT

    aa = PT.SetTimerEvent(Val(Text6.Text))
    Select Case aa
        Case -1
            OkToInitiate = True
        Case False
            OkToInitiate = False
    End Select

End Sub

不过,我猜想有一个更好的地方来初始化 TimerEvents 变量。

如果要停止处理事件,请将变量设置为 Nothing。

Set TimerEvents = Nothing

【讨论】:

  • 谢谢菲尔!将Publiic WithEvents 放在表单的General 部分中,将TimerEvent 放入下拉列表中。现在它可以正常工作了!我还在 Form_Load 例程中放置了 Set TimerEvents =PT。
【解决方案3】:

您似乎不愿意分享您的 .Net 代码。为了给您一个工作示例,请尝试实现以下示例,该示例将详细说明创建暴露给 COM 的工作类库的步骤。我没有安装 VB6,但使用 MS Office VBA 对此进行了测试。据我了解,VBA 是语言,VB6 是语言的宿主。有关这方面的更多信息,请参阅:Difference between Visual Basic 6.0 and VBA

第 1 步:创建 COM 类库。 以管理员身份启动 Visual Studio。这样 VS 就可以在注册表中注册 COM 组件。 从“空项目 (.Net Framework) - Visual Basic”模板创建一个新项目。我将项目命名为“Demo COM Exposed Event”。转到项目属性 - 应用程序选项卡并将“应用程序类型”设置为“类库”。您还可以清除“根命名空间”框以强制所有内容都在全局命名空间中,但这不是强制性的,因为 COM 不识别命名空间。

接下来打开构建配置管理器,为项目定义和激活 X86(32 位)配置。

接下来配置项目以注册 COM 类。这是在项目属性中的“编译选项卡”上完成的。

下面的 VB.Net 代码定义了COMDemoClass。此类公开了一个事件 (Event1)、一个引发事件的方法 (SingleFireEvent1) 和一个布尔属性 (AutoFireEvent1),它启动/停止可以触发 Event1 的计时器。

Imports System.Runtime.InteropServices
Imports System.Timers

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

#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 = "db10634a-91fe-4cde-a2da-5421578e6142"
    Public Const InterfaceId As String = "22d6da07-b40c-4a04-9551-9c375bf99c06"
    Public Const EventsId As String = "2096fe58-d50c-4d19-9039-a6ad4193385c"
#End Region

    <ComVisible(False)>
    Delegate Sub _Event1(arg As String)
    Public Event Event1 As _Event1
    Private tmr As New System.Timers.Timer With {.AutoReset = True, .Interval = 500, .Enabled = False}
    Private _AutoFireEvent1 As Boolean

    ' 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()
        AddHandler tmr.Elapsed, AddressOf tmr_Elapsed
    End Sub

    Private Sub tmr_Elapsed(sender As Object, e As ElapsedEventArgs)
        SingleFireEvent1(DateTime.Now.ToLongTimeString())
    End Sub

    Public Sub SingleFireEvent1(Optional arg As String = "Hello")
        RaiseEvent Event1(arg)
    End Sub

    Public Property AutoFireEvent1 As Boolean
        Get
            Return _AutoFireEvent1
        End Get
        Set(value As Boolean)
            If value <> _AutoFireEvent1 Then
                _AutoFireEvent1 = value
                tmr.Enabled = value
            End If
        End Set
    End Property
End Class

此时,构建项目以生成 Dll 和类型库。这些应该可以在解决方案资源管理器中查看。

第 2 步:在 VBA 代码中使用该库。

在 VB IDE 中,添加对第 1 步中创建的类型库的项目引用。

添加一个“类模块”并将其命名为“ExposeVBNetLib”并将此代码添加到模块中。请注意,您可以将类似的代码添加到表单或其他类类型模块;您只是不能在标准模块中定义WithEvents 变量。

    Option Explicit

    Private WithEvents vbNetCOMClass As Demo_COM_Exposed_Event.COMDemoClass

    Private Sub Class_Initialize()
       Set vbNetCOMClass = New Demo_COM_Exposed_Event.COMDemoClass
    End Sub

    Private Sub Class_Terminate()
       Set vbNetCOMClass = Nothing
    End Sub

To test the VBA class, add the following code to a standard Module.

Option Explicit

Private instance As ExposeVBNetLib  ' a variable to keep the class reference alive between methods

Sub CreateAndLaunch()
   Set instance = New ExposeVBNetLib
   instance.SingleFireEvent1 "It works"
   instance.AutoFireEvent1 = True
End Sub

Sub ShutDown_instance()
   If Not (instance Is Nothing) Then
      instance.AutoFireEvent1 = False
      Set instance = Nothing
   End If
End Sub

Private Sub vbNetCOMClass_Event1(ByVal arg As String)
   Debug.Print arg
End Sub

Public Property Get AutoFireEvent1() As Boolean
   AutoFireEvent1 = vbNetCOMClass.AutoFireEvent1
End Property

Public Property Let AutoFireEvent1(arg As Boolean)
  vbNetCOMClass.AutoFireEvent1 = arg
End Property

Public Sub SingleFireEvent1(arg As String)
   vbNetCOMClass.SingleFireEvent1 arg
End Sub

该类订阅 COM 类 COMDemoClassEvent1,并在方法 vbNetCOMClass_Event1 中处理事件。此方法将接收到的参数打印到“立即窗口”。

第 3 步 - 测试

在标准 VBA 模块中,放置以下代码:

Option Explicit

Private instance As ExposeVBNetLib  ' a variable to keep the class reference alive between methods

Sub CreateAndLaunch()
   Set instance = New ExposeVBNetLib
   instance.SingleFireEvent1 "It works"
   instance.AutoFireEvent1 = True
End Sub

Sub ShutDown_instance()
   If Not (instance Is Nothing) Then
      instance.AutoFireEvent1 = False
      Set instance = Nothing
   End If
End Sub

运行CreateAndLaunch 方法启动代码。运行ShutDown_instance方法停止代码。

最后,您应该能够在 Visual Studio 中调试您的 .Net 代码,方法是将项目配置为启动 VBA 主机。就我而言,这是 Excel 和您的 VB6。这是通过在“启动选项”下设置“启动外部程序”从项目属性下的“调试选项卡”完成的。

【讨论】:

  • 感谢 TnTinMn!我能够使用 Phil Jollans 方法来完成任务。不是我不愿意分享,它很大,我不确定它是否相关。感谢您花时间逐步整理此内容。我要研究它并确保我理解它,这样如果再次出现,我可以解决它。
猜你喜欢
  • 2011-12-01
  • 1970-01-01
  • 2012-09-10
  • 2012-08-10
  • 2011-07-17
  • 2021-12-20
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
相关资源
最近更新 更多