RSS

COM Interop: Really Raising Events Handled by a COM Sink

06 Jul
This document correctly outlines how to use COM Interop to sink .NET events in a COM object. If you are looking for an explanation as to why this works and the MSDN article, Raising Events Handled by COM Sink, does not, please refer to this document. Other than that explanation, the information here is a rip of the information in the Raising Events Handled by COM Sink article, with the appropriate modifications made to it. Additionally, I found it a pain that the MSDN article neglected a C++ ATL COM client listing, so I added one here for those who prefer it. I am unable to confirm that the VB listings work as they should (though I am confident that they do). If you can confirm this and want to email me to let me know and I’ll give you credit on this page. Enjoy.

If you are not familiar with the delegate-based event model provided by the .NET Framework, see Handling and Raising Events. For specific details that apply to this topic, see Raising an Event in the same section.

The .NET Framework provides a delegate-based event system to connect an event sender (source) to an event receiver (sink). When the sink is a COM client, the source must include additional elements to simulate connection points. With these modifications, a COM client can register its event sink interface in the traditional way by calling the IConnectionPoint::Advise method. (Visual Basic hides connection point details, so you do not have to call these methods directly.)

To interoperate with a COM event sink

  1. Define the event sink interface in managed code. This interface can contain a subset of the events sourced by a managed class.
  2. Apply the ComSourceInterfacesAttribute to connect the event sink interface to the managed class.
  3. Export the assembly containing the class to a type library. Use Tlbexp.exe or an equivalent API to export the assembly.
  4. Implement the event sink interface in COM.

For COM clients that sink events, implement the event sink interface defined by the event source in its type library. Then use the connection point mechanism to connect the sink interface to the event source.

The following example shows a Managed Server as the event source and a COM Client as the event sink. The managed server declares ButtonEvents as an event sink interface and connects the interface to the Button class. The unmanaged client creates an instance of the Button class and implements the event sink interface.

Managed server (event source)

[Visual Basic]
Option Explicit
Option Strict

Imports System
Imports System.Runtime.InteropServices

Namespace EventSource
Public Delegate Sub ClickDelegate(x As Integer, y As Integer)

Public Delegate Sub ResizeDelegate()
Public Delegate Sub PulseDelegate()

' Step 1: Defines an event sink interface (ButtonEvents) to be
' implemented by the COM sink.
<GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967"), _

InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface ButtonEvents
<DispID(1)>
Sub Resize()
<DispID(2)>
Sub Click(x As Integer, y As Integer)

<DispID(3)>
Sub Pulse()
End Interface

' Step 2: Connects the event sink interface to a class
' by passing the namespace and event sink interface
' GetType(ButtonEvents).

<ComSourceInterfaces(GetType(ButtonEvents))> _
Public Class Button
Public Event Click As ClickDelegate
Public Event Resize As ResizeDelegate
Public Event Pulse As PulseDelegate



Public Sub CauseClickEvent(x As Integer, y As Integer)
RaiseEvent Click(x, y)
End Sub

Public Sub CauseResizeEvent()
RaiseEvent Resize()

End Sub

Public Sub CausePulse()
RaiseEvent Pulse()
End Sub
End Class
End Namespace
[C#]
using System;
using System.Runtime.InteropServices
;
namespace EventSource
{
public delegate void ClickDelegate(int x, int y);
public delegate void ResizeDelegate();
public delegate void PulseDelegate();

// Step 1: Defines an event sink interface (ButtonEvents) to be

// implemented by the COM sink.
[GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ButtonEvents

{
[DispId(1)]
void Resize();
[DispId(2)]
void Click(int x, int y);
[DispId(3)]
void Pulse();
}
// Step 2: Connects the event sink interface to a class

// by passing the namespace and event sink interface
// typeof(ButtonEvents).
[ComSourceInterfaces(typeof(ButtonEvents))]
public class Button
{
public event ClickDelegate Click;

public event ResizeDelegate Resize;
public event PulseDelegate Pulse;

public Button()
{
}
public void CauseClickEvent(int x, int y)
{

Click(x, y);
}
public void CauseResizeEvent()
{
Resize();
}
public void CausePulse()
{
Pulse();
}

}
}

COM client (event sink)

[Visual Basic]
' This Visual Basic 6.0 client creates an instance of the Button class and
' implements the event sink interface. The WithEvents directive
' registers the sink interface pointer with the source.

Public WithEvents myButton As Button

Private Sub Class_Initialize()
Dim o As Object
Set o = New Button
Set myButton = o
End Sub
' Events and methods are matched by name and signature.

Private Sub myButton_Click(ByVal x As Long, ByVal y As Long)
MsgBox "Click event"
End Sub

Private Sub myButton_Resize()
MsgBox "Resize event"
End Sub

Private Sub myButton_Pulse()

End Sub
[Visual C++ ATL COM]
// The following code is in the header file of my Simple ATL object.
#import "EventSource.tlb" raw_interfaces_only, named_guids, no_namespace

// The class must inherent from the IDispEventImpl interface
class ATL_NO_VTABLE CTestSink : public IDispEventImpl<0, CTestSink, &DIID_ButtonEvents, &LIBID_EventSource, /*wMajor =*/ 1, /*wMinor =*/ 0>


CComPtr<ICLASS1> m_pApp;

void __stdcall OnManagedEvent() { /*TODO: Implement Event Code Here*/ }

BEGIN_SINK_MAP(CTestSink)
SINK_ENTRY_EX(0, DIID_IClass1Events, 1, OnManagedEvent)
END_SINK_MAP()


// The following code is in the Source file of my Simple ATL Object
STDMETHODIMP CTestSink::Init(void){
HRESULT hr;
if( FAILED( hr = CoCreateInstance(CLSID_Class1,NULL, CLSCTX_SERVER, IID_IClass1, (void **) &m_pApp) ) ) {

return S_FALSE;
}
else{
if( FAILED( hr = DispEventAdvise( (IUnknown*)m_pApp ) ) ) {
return S_FALSE;
}
}
return S_OK;
}

STDMETHODIMP CTestSink::End(void){
HRESULT hr;

if( m_pApp ){
if( FAILED( hr = DispEventUnadvise( (IUnknown*)m_pApp ) ) ) {
return S_FALSE;
}
}
return S_OK;
}

STDMETHODIMP CTestSink::FireManagedEvent(void){
if( m_pApp ){

m_pApp->Resize();
}
return S_OK;
}

The information on this page is provided "AS IS" with no warranties, and confers no rights.

About these ads
 
3 Comments

Posted by on 2005/07/06 in .Net

 

3 responses to “COM Interop: Really Raising Events Handled by a COM Sink

  1. Mark

    2009/06/20 at 11:23 am

    This is *almost* exactly what I need to do, and I’m finding it practically impossible to find the answer to my variation of the problem. I need to raise events from a C# COM visible DLL to a VB6 application, and my events need to pass arguments defined in a custom EventArgs class. I’ve been trying to define these events in the manner of MyEventWithArgs(object sender, MyEventArgs e)Is this not possible for some reason? Like I said, I’ve searched for hours the last 2 days and have found a couple of other posts with the same question, but never an answer. If you can provide any insight it would be most appreciated.

     
  2. Jason

    2009/06/23 at 2:09 pm

    Mark, It’d be easier to catch the events within your .NET component and then make a call to a subscriber (VB6 Application) assembly. I find that events are best handled within the eventing model (.net or vb6) that it originates from. Straight API calls made back and forth, when the event is being handled, is a far easier model to work with.HTH,JH

     
  3. bosspy review

    2014/03/04 at 9:48 am

    I couldn’t resist commenting. Pefectly written!

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: