2
votes

I wrote my code like the example below:

public delegate void ClickDelegate(int x, int y);
public delegate void PulseDelegate();

[Guid("39D5B254-64DB-4130-9601-996D0B20D3E5"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IButton
{
  void Work();
}

// 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
{
    void Click(int x, int y);
    void Pulse();
}

// Step 2: Connects the event sink interface to a class 
// by passing the namespace and event sink interface.
// ("EventSource.ButtonEvents, EventSrc").
[ComSourceInterfaces(typeof(ButtonEvents))]
public class Button : IButton
{
    public event ClickDelegate Click;
    public event PulseDelegate Pulse;

    public Button() { }

    public void CauseClickEvent(int x, int y) { Click(x, y); }
    public void CausePulse() { Pulse(); }

    public void Work() { /* Do some stuff */ }
}

This works fine with VB. When I define it like:

Dim WithEvents obj As Button

But I want to define it with the Interface like:

Dim WithEvents obj As IButton

This is not working 'cause the events are not visible from the IButton interface.

Is there a way to do this?

1
No, in a type library, the source (or event) interfaces are declared for a coclass.acelent
Excellent example, which helped me immensely in migrating a VB6 user control to managed code.R.J. Dunnill

1 Answers

1
votes

A variable that connects to an object's events must be declared to be of the type (CoClass) of the object (Button in your example). An interface (IButton in your example) doesn't know anything about the events, and it cannot be used to request them.

This is the way I like to think about it:

An interface is something two objects agree on using so the "client" can send commands to the "server" ("server, do XYZ!"). An Event is just a different interface that the two objects also agree on using, but for the opposite: that is, for the "server" object to send commands to the "client".

Whether a given Event interface is supported is a property of the object, not a property of whatever interface(s) the object might support. The server object says: "Give me a ButtonEvents interface pointer, and I'll use it to tell you when the button has been clicked". It's not the IButton interface that makes that offer.

That's also why you have to apply the [ComSourceInterfaces] attribute to the class Button, not to the interface IButton. The Button CoClass is the one that makes the offer.

The thing that makes events look special or weird is that we need to use a somewhat complicated and confusing dance ("Connection Points") to pass the event's interface pointer around. WithEvents is the way to ask VB6 to "do the dance" for you.