Exposing Com Events – C#


With C#’s declerative programming ability it’s relatively easy to expose the types to COM environment. But there are a few thing you must pay attention if you want to raise events from your managed COM component. I’ll not go into the details of COM programming with C# but just focus on events for now.


Thanks to ComSourceInterfaces attribute; by using this attribute you can easily point to an interface which has the event declerations for your COM type. But before using that attribute you must have an interface to declare your type’s events. (Keep in mind events are “dispatched”, which means your interface that will contain the event declerations should implement IDispatch)


Here is a sample interface:


[ComVisible(true)] //Exposed


[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //Our managed interface will be IDispatch


public interface IMathEvents {


    void OnOperationCompleted(string message);


}


And of course we need a class that will implement that interface and expose our event to COM. Here it is:


[ClassInterface(ClassInterfaceType.None)]


[ComSourceInterfaces(typeof(IMathEvents))]  //Our event source is IMathEvents interface


public class Math : ServicedComponent , IMath  /*IMath interface just declares a method named Add */


  {


  public int Add(int x, int y) {


    if (null != this.OnOperationCompleted)


    this.OnOperationCompleted(“Operation completed”);


    return x + y;


}


[ComVisible(false)]


public delegate void OperationCompletedDel(string message); //No need to expose this delegate


public event OperationCompletedDel OnOperationCompleted;


}


We need the ClassInterface attribute and we need to set it to None. If we dont do that we’ll end up with a class and two different interfaces one of which has the method and the other one having the event. And for example if you need to access that class from VB you’ll end up with two interfaces which may be confusing such as;


Private WithEvents ? obj as IMath <— Cannot access events ??


Private WithEvents ? obj as IMathEvents <– Cannot access the Add method ??


But obviously we need to be able to access both the event and members 😉 thanks to ClassInterface attribute. So we can:


Private WithEvents obj as Math


 


 

Comments (10)

  1. ps john says:

    this is the closest article I found to solving this problem, but it doesn’t work.  I assume that you forgot to use [ComVisible(true)] for class Math, but it still throws automation type not supported when compiling VB code.

  2. ps john says:

    Actually, the automation type error was my bug.  This solution works for me but I have to set [ComVisible(true)] for all interfaces and the class that implements them. Thanks Murat.

  3. muratkar says:

    I didnt have time to fix the code but nice to hear that you have found the solution John.

  4. Bill says:

    So how can you hook up multiple clients that are all able to receive an OperationCompleted event generated from any other client?

  5. cealex says:

    sorry but this doesn't work , period!

    i created  a test app with the code above,

    namespace COMTest

    {

       [ComVisible(true)] //Exposed

       [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //Our managed interface will be IDispatch

       public interface IMathEvents {

           void OnOperationCompleted(string message);

       }

       //And of course we need a class that will implement that interface and expose our event to COM. Here it is:

       [ClassInterface(ClassInterfaceType.None)]

       [ComSourceInterfaces(typeof(IMathEvents))]  //Our event source is IMathEvents interface

       [ComVisible(true)]

       public class Math : ServicedComponent , IMathEvents  /*IMath interface just declares a method named Add */

       {

           [ComVisible(true)]

           public delegate void OperationCompletedDel(string message); //No need to expose this delegate

           [ComVisible(true)]

           public event OperationCompletedDel OnOperationCompleted;

           //void OnOperationCompleted(string message);

           public int Add(int x, int y)

           {

               if (null != this.OnOperationCompleted)

                   this.OnOperationCompleted("Operation completed");

               return x + y;

           }

       }

    }

    why declarating  IMathEvents and don't use ?

    where are the interface "IMath interface just .."   ?

    when i compile, the error is :

    Error 1 'COMTest.Math' does not implement interface member 'COMTest.IMathEvents.OnOperationCompleted(string)'

    can you enlight me ?

  6. Terren says:

    It really works! Thanks for the sharing!

    regards,

    Terren

  7. muratkar says:

    cealex. As stated on the comments IMath has an Add method only, you could create that interface yourself. And for the IMathEvents interface; it's beng implemented by the Math class but not inherited from it. Check out the ComSourceInterfaces attribute. If you stick with the original code I dont see a reason why it shouldn't work.

    For more info please take a look at msdn.microsoft.com/…/dd8bf0x3(v=VS.100).aspx

  8. muratkar says:

    Bill. Looks like you want to have a server (singleton) with multiple clients. (I might be wrong, but if I am please correct me) then I'd say either set the min pool = max pool = 1 and make sure there's one component serving all clients or behind scenes have a singleton class instance which is doing the actual job (not necessarily exposed to COM) and being wrapped by dummy COM servers which are just exposing the same functionality (like events) to the clients. That should solve the problem.

  9. Good one. As John mentioned, even class has to be made COM Visible.