VB6 .NET Interop参数作为字符串传递

时间:2011-11-04 05:06:57

标签: .net vb6 interop com-interop

我有一个.NET界面

<System.Runtime.InteropServices.GuidAttribute("0896D946-8A8B-4E7D-9D0D-BB29A52B5D08"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IEventHandler
    Sub OnEvent(ByRef sender As Object, ByRef e As Object)
End Interface

在导出的类型库中。

VB6代码引用此tlb并实现此接口。

VB6代码创建了它的实现实例并将其传递给.NET。

.NET调用OnEvent。

VB6将事件发送得很好...但发件人和e参数值是字符串,而不是对象到达那里...字符串值是类型的全名......

VB6代码:

Implements Interop.IEventHandler

Private Sub IEventHandler_OnEvent(ByRef sender As Variant, ByRef e As Variant)
    Dim id
    id = e.Person.Id

    ' The weird thing here:
    ' e = "XYZ.Tasks.PersonTaskEventArgs"
    ' sender = "XYZ.Tasks.PersonUIManager"
    ' The values of the arguments are the NAMEs of the actual object values' types...
End Sub

触发事件的代码相当简单。我有一个带有字典的COM类,它可以注册处理程序并触发事件。

<ComClass(ComRegistrar.ClassId, ComRegistrar.InterfaceId, ComRegistrar.EventsId>
Public Class ComRegistrar

   Private Shared ReadOnly _eventHandlers As New Dictionary(Of String, List(Of IEventHandler))


   ' This is called by .NET code to fire events to VB6
   Public Shared Sub FireEvent(ByVal eventName As String, ByVal sender As Object, ByVal e As Object)
        For Each eventHandler In _eventHandlers(eventName)
                eventHandler.OnEvent(sender, e)
        Next
   End Sub

   Public Sub RegisterHandler(ByVal eventName As String, ByVal handler As IEventHandler)
        Dim handlers as List(Of IEventHandler)
        If Not _eventHandlers.TryGetValue(eventName, handlers)
             handlers = New List(Of IEventHandler)
             _eventHandlers(eventName) = handlers
        End If
        handlers.Add(handler)
   End Sub

End Class

.NET代码看起来像

Public Class PersonEventArgs
     Inherits System.EventArgs


' Some properties
End Class

Public Class MyControl
     Inherits UserControl

' Stuff
End Class


ComRegistrar.FireEvent("PersonSelected", Me, New PersonEventArgs With { Some stuff })

如果我使用实现IEventHandler的.NET类连接相同的代码,则参数可以顺利通过。

更新:如果我将OnEvent的ByRef参数更改为ByVal,则没有任何区别。我确定我尝试传递的两种类型来自标记为ComVisible的程序集。

这里出了什么问题?

1 个答案:

答案 0 :(得分:1)

好的,既然我们已经确认参数是作为(COM)对象而不是字符串传递的,那么最后要解决的谜是为什么会出现错误13,类型不匹配。

我会在两个方面探讨这个问题:首先,使用TypeName()来获取VB6认为它得到的对象(例如TypeName(e.Person))。然后,考虑VB6是否真的有办法获得该对象的成员 - 一个好的起点是使用VB6 IDE中的对象浏览器。如果对象类显示但尚未公开任何成员(即使启用了显示隐藏成员),则此类可能未正确映射为COM对象。

====上一页====

对于咯咯笑声,当你停止或断点进入Sub IEventHandler_OnEvent()时,进入VB6 IDE的立即窗口,看看这些是否合法:

?sender.ToString
?e.ToString

另外,放弃使用ByRef并使用ByVal;除非您真的想要更改引用的值与实例的状态,否则您不需要ByRef。

我的怀疑是你真的得到了物品;你看到的表观字符串是ToString()成员的结果,它以某种方式作为COM默认属性。

检查上述怀疑的一种方法是在VB6 IDE的立即窗口中尝试这些(在IEventHandler_OnEvent()中停止执行时):

?typename(sender)
?typename(e)

如果其中任何一个真的是字符串,则Typename应显示String。

====上一页====

杰夫,请告诉我这些VB6和.NET项目是否是您问题的有效案例研究。

在.NET中,我创建了一个DLL项目,它有一个接口和一个类,都有COM暴露:

==== .NET,文件IComFun.vb

Imports System.Runtime.InteropServices

<GuidAttribute("b3f1ab4f-dc99-4990-ade1-8a4833d8bcab"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IComFun
    Sub OnFoo(ByVal sender As Object, ByVal e As Object)
End Interface

==== .NET,文件ComFun.vb

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

#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 = "257a4ed2-1f27-4d8e-bcf2-f90828ae649b"
    Public Const InterfaceId As String = "4431a515-3c01-4fff-b281-20ec8ad0c0b6"
    Public Const EventsId As String = "9b44258f-142e-4dd0-bc22-6d24d1f58657"
#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


    Public Sub Ping(ByVal sender As Object, ByVal e As Object)
        DirectCast(sender, IComFun).OnFoo(sender, e)
    End Sub

End Class

在VB6中,我创建了一个标准的EXE项目,通过TLB文件引用基于.NET的COM库,有一个表单(包含一个按钮)和一个类如下:

==== VB6,文件Form1.frm

Dim x As Class1
Dim y As Class1
Dim z As ComFun.ComFun

Private Sub cmdPing_Click()

  If x Is Nothing Then
    Set x = New Class1
  End If

  If y Is Nothing Then
    Set y = New Class1
  End If

  If z Is Nothing Then
    Set z = New ComFun.ComFun
  End If

  z.Ping x, y

End Sub

==== VB6,File Class1.cls

Implements ComFun.IComFun

Private Sub IComFun_OnFoo(ByVal sender As Variant, ByVal e As Variant)

  Stop

End Sub

我做了上面的项目并编译了它们,然后运行了VB6 EXE(在IDE中)并单击了按钮。当我到达IComFun_OnFoo中的Stop语句时,我得到的是实际的对象,而不是字符串 - 换句话说,它的工作方式与您需要它一样。我还查看了调用堆栈(在VB6 IDE中)以确认正在调用非基本代码。

所以,我并没有真正解决你的问题,但我希望能更好地了解情况。