我有一个.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
的程序集。
这里出了什么问题?
答案 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中)以确认正在调用非基本代码。
所以,我并没有真正解决你的问题,但我希望能更好地了解情况。