HK1
HK1

Reputation: 12230

VB.Net Com Interop - Sink COM Events to VBA Client

I'm programming a COM interop DLL in VB.NET and I need the events to be exposed to a VBA COM client. I just can't wrap my head around this one.

This is a COM wrapper for a .NET FTP library so don't get too confused by the fact that I'm raising a BytesTransferred event on a BytesTransferred event.

As I best recall, this code works but simply fails to show any events in the COM client when I use the object browser. It also fails to compile when I try to Dim my variable WithEvents:

Public Interface IFTP
    Event BytesTransferred(ByVal ByteCount As Long, ByVal RemoteFileName As String)
End Interface

'Here's the relevant part of my class:
Public Interface IFTP
    Event BytesTransferred(ByVal ByteCount As Long, ByVal RemoteFileName As String)
End Interface

Here's the relevant part of my class:

<ClassInterface(ClassInterfaceType.None)> _
    Public Class FTP : Implements IFTP

    Public Sub New()
        'This needed for com interop
    End Sub

    Public Event BytesTransferred(ByVal ByteCount As Long, ByVal RemoteFileName As String) Implements IFTP.BytesTransferred

    Private Sub fCon_BytesTransferred(ByVal sender As Object, ByVal e As EnterpriseDT.Net.Ftp.BytesTransferredEventArgs) Handles fCon.BytesTransferred
        RaiseEvent BytesTransferred(e.ByteCount, e.RemoteFile)
    End Sub

End Class

I've also tried something like this but I think I'm missing something here because it doesn't compile. I see an error that says I've failed to implement Sub BytesTransferred for interface IFTP:

Public Delegate Sub BytesTransferredDelegate(ByVal ByteCount As Long, ByVal RemoteFileName As String)

Public Interface IFTP
    <DispId(1)> _
    Sub BytesTransferred(ByVal ByteCount As Long, ByVal RemoteFileName As String)
End Interface

'Here's the relevant part of my class:

<ClassInterface(ClassInterfaceType.None)> _
    <ProgId("MyTestClass.FTP")> _
    <ComSourceInterfaces(GetType(IFTP))> _
Public Class FTP : Implements IFTP
    Public Event BytesTransferred As BytesTransferredDelegate

    Public Sub New()
        'This needed for com interop
    End Sub

    Private Sub fCon_BytesTransferred(ByVal sender As Object, ByVal e As EnterpriseDT.Net.Ftp.BytesTransferredEventArgs) Handles fCon.BytesTransferred
        RaiseEvent BytesTransferred(e.ByteCount, e.RemoteFile)
    End Sub

End Class

Upvotes: 4

Views: 2595

Answers (1)

HK1
HK1

Reputation: 12230

Solution:
1) Declare Delegate Subs (with arguments if needed) at top of code module (inside your namespace if you are using one)
2) Create a separate interface for your events, as shown below. Be sure to include the IDispatch declaration
3) No need to put anything for your events in your primary interface.
4) Use the ComSourceInterfaces declaration with your class declaration
5) Inside your class declare your events using "As" to point to your Delegate Sub

As mentioned by Hans Passant, be sure to use data types that are compatible with VB6/VBA. Long is not a compatible data type.

Here's my code that now works:

Public Delegate Sub BytesTransferredDelegate(ByVal ByteCount As Long, _
                    ByVal RemoteFileName As String)
Public Delegate Sub OnUploaded()

<InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IFTPEvents
    <DispId(1)> _
    Sub BytesTransferred(ByVal ByteCount As Double, _
                         ByVal RemoteFileName As String)
    <DispId(2)> _
    Sub Uploaded()
End Interface

Public Interface IFTP
    'Subs, Functions, Properties go here
    'No subs, functions, or events need to be declared here
    'to make our events work properly in COM
End Interface

<ComSourceInterfaces(GetType(IFTPEvents)), ClassInterface(ClassInterfaceType.None)> _
Public Class FTP : Implements IFTP
    Public Event BytesTransferred As BytesTransferredDelegate
    Public Event Uploaded As OnUploaded

    Public Sub New()
        'This needed for com interop
    End Sub

    Private Sub fCon_BytesTransferred(ByVal sender As Object, ByVal e As EnterpriseDT.Net.Ftp.BytesTransferredEventArgs) Handles fCon.BytesTransferred
        RaiseEvent BytesTransferred(e.ByteCount, e.RemoteFile)
    End Sub

    Private Sub fCon_Uploaded(ByVal sender As Object, ByVal e As EnterpriseDT.Net.Ftp.FTPFileTransferEventArgs) Handles fCon.Uploaded
        RaiseEvent Uploaded()
    End Sub

End Class

Here's the sources I used to solve this problem:
http://msdn.microsoft.com/en-us/library/dd8bf0x3%28v=VS.90%29.aspx
http://www.codeproject.com/KB/COM/cominterop.aspx#UnmanagedSinks
http://www.developerfusion.com/tools/convert/csharp-to-vb/

Upvotes: 7

Related Questions