I have a .NET interface
<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
in an exported type library.
The VB6 code references this tlb and implements this interface.
The VB6 code creates an instance of it’s implementation and passes it to .NET.
.NET calls OnEvent.
VB6 picks it up the event fine…but the sender and e argument values are strings, not objects when it gets there… The string values are the full names of the types…
The VB6 code:
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
The code that fires the event is fairly trivial. I have a COM class with a dictionary that registers handlers and fires events.
<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
The .NET code looks like
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 })
If I wire up the same code using a .NET class that implements IEventHandler, the arguments come through without a problem.
UPDATE: If I change my ByRef parameters for OnEvent to ByVal, it makes no difference. I’m sure the two types I’m trying to pass are from an assembly marked as ComVisible.
What’s going wrong here?
OK, since we’ve confirmed that the parameters are coming through as (COM) objects and not Strings, the last mystery to solve is why do you get an Error 13, Type mismatch.
I would explore this issue on two fronts: First, use TypeName() to get at the object VB6 thinks its got (e.g. TypeName(e.Person)). Then, think about whether VB6 really has the means to get at the members of that object – a good place to start is with the Object Browser in the VB6 IDE. If the object class shows up in that yet exposes no members (even with Show Hidden Members enabled), then this class probably isn’t being properly mapped out as a COM object.
==== Previous ====
For the giggles, when you Stop or break-point into Sub IEventHandler_OnEvent(), go into the Immediate window of the VB6 IDE and see if these are legal:
Also, give up on using ByRef and go with ByVal; unless you’re really trying to change the value of a reference versus the state of an instance, you don’t need ByRef.
My suspicion is that you are really getting the objects; the apparent string you see is the result of the ToString() member, which is somehow made as the COM default property.
One way to check the above suspicion is to try these in the Immediate window of the VB6 IDE (when execution is stopped within IEventHandler_OnEvent()):
Typename should show String if either of these is really a string.
==== Previous ====
Jeff, please let me know if these VB6 and .NET projects are a valid case study of your issue.
In .NET, I created a DLL project having one interface and one class, both with COM exposure:
==== .NET, File IComFun.vb
==== .NET, File ComFun.vb
In VB6, I created a standard EXE project with references to the .NET-based COM library via the TLB file, having one form (containing one button) and one class like so:
==== VB6, File Form1.frm
==== VB6, File Class1.cls
I made the above projects and compiled them, and then ran the VB6 EXE (in the IDE) and clicked the button. When I got to the Stop statement in IComFun_OnFoo, I was getting the actual objects and not strings – in other words, it worked the way you needed it to work. I also looked at the call stack (in VB6 IDE) to confirm that non-Basic code was being called.
So, I didn’t really solve your problem, but I’m hoping to better understand the situation.