Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VB6/COM Interop: where do these events come from?

I have written a COM-visible class library in C# 4.0 which I'm consuming with VB6. The thing works, only if I open up the VB6 object browser and look at the members exposed, I'm seeing an event for each and every single exposed member... but the C# code doesn't define any of them.

Is this normal? Am I doing something wrong?

[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyClass))]
public class MyClass : IMyClass
{
    public void DoSomething(string someParam)
    {
        ...
    }
}

public interface IMyClass
{
    void DoSomething(string someParam);
}

The assembly is signed with a strong name key and AssemblyInfo.cs has the [assembly: ComVisible(true)] attribute set, but I'm not sure it has anything to do with the issue.

When I look at the object browser in VB6, I would be expecting to see DoSomething(string) as a member of MyClass, and I do, however I'm also seeing an event with a matching signature for every exposed method, like Event DoSomething(someParam As String) as a member of MyClass.

Even more puzzling (to me at least), properties also have a "matching" event (can only tell from the little lightning icon though) - if MyClass defined a property like this:

public string SomeProperty { get; set; }

The VB6 object browser would say the "event" is defined as Property SomeProperty As String, which leaves me flabbergasted - how does a "property" 1) gets duplicated and 2) the duplicate gets displayed with an "event" icon in the object browser? The same applies to get-only properties, which have their read-only "property/event" counterpart.

Where do these events come from and how do I get rid of them?

UPDATE An image is worth a thousand words:

COM Interop - bogus events

UPDATE The wrong thing was the ComSourceInterfaces attribute which was mistakenly being used in place of a ComDefaultInterface attribute. Swapping the former for the latter gives the expected result:

COM Interop - correct members

like image 592
Mathieu Guindon Avatar asked May 17 '13 01:05

Mathieu Guindon


2 Answers

By passing typeof(IMyClass) as an argument to the ComSourceInterface attribute you're saying that everything in the IMyClass is an event.

If you don't want an event interface for your class remove the ComSourceInterface attribute.

If you do want to expose events from your C# class to VB then do the following:

When you create a COM visible class you'll also want to create an interface that defines just the event handlers for your class. Your class should be decorated with the COMSourceInterface specifying your event handler interface and should define your events and implement the event handler interface. See How To: Raise Events Handled by a COM sink for another example.

[GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface MyEvents
{
    void ConnectedEvent(string state);
}


[ComSourceInterfaces(typeof(MyEvents))]
public class MyClass
{
    public event Action<string> ConnectedEvent;

    public MyClass() { }

    public void DoSomething(string state)
    { 
        if (ConnectedEvent != null)
            ConnectedEvent(state);
    }
}

See also: Murat's Corner: Exposing COM Events

like image 172
Dan Busha Avatar answered Nov 01 '22 18:11

Dan Busha


You are basically finding out that there isn't anything special about events in COM. Like anything in COM, events are backed by an interface. The only thing special about an interface that specifies event methods is that it is marked with the [source] attribute in the type library. Which is all that the [ComSourceInterfaces] attribute does, recognized by Tlbexp.exe when it generates the type library.

Nor is there anything particularly special about properties in COM. They work just like they do in .NET, they are implemented with methods. A getter and a setter method.

So VB6 looks at your type library and is happy about a class that has events since it has an interface with the [source] attribute. And is happy about that interface having methods, all they can ever have, so it assumes those are the methods that run when the event is raised. It isn't otherwise smart enough to recognize that those methods are also the accessors for a property, it assumes that the type library author knows what he's doing.

Events are called "connection points" in COM. Google IConnectionPoint to learn more about it. If you ever create a WinRT component with custom event accessors then you'll also see that COM events have little in common with .NET events.

Anyhoo, the workaround is simple, only use [ComSourceInterface] when you raise events.

like image 8
Hans Passant Avatar answered Nov 01 '22 20:11

Hans Passant