Search This Blog

Monday, July 16, 2012

Exposing COM Events

Hi,
This is a quick sample code that shows you how to expose events to COM.
Just remember to "register the assembly for COM interop" and please do not tick "Make Assembly COM Visible". We use the ComVisible attribute to decise what to make visible for COM interop.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;





namespace MyCOMEvents01
{
    //  To expose properties and methods to COM, you must declare them on the class 
    //  interface and mark them with a DispId attribute, and implement them in the class. 
    //  The order in which the members are declared in the interface is the 
    //  order used for the COM vtable.
    //  ex:
    //  [DispId(1)]
    //  void Init(string userid , string password);
    //  [DispId(2)]
    //    bool ExecuteSelectCommand(string selCommand);

    //Class Interface
    [Guid("09a22bef-9826-4ea6-8e12-83adbbc0efd1"),
     ComVisible(true),
     InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IPerson
    {
        [DispId(1)]
        string Id { get; set; }

        [DispId(2)]
        string Name { get; set; }

        [DispId(3)]
        double Age { get; set; }
    }



    // To expose events from your class, you must declare them on the events 
    // interface and mark them with a DispId attribute. 
    // The class should not implement this interface. 

    //Events Interface
    [Guid("94d63c5e-125e-4f7d-aa0a-0d62dd4dc4fd"),
     ComVisible(true),
     InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IPersonEvents
    {
        [DispId(101)]
        void OnAfterNameChange(object sender, string name);

        [DispId(102)]
        void OnBeforeNameChange(object sender, string newName, ref bool cancel);
    }



    //The Class can also implement other interfaces. But only
    //the first one will be exposed to COM.
    //COM Class do not support inheritance beyond interface implementation
    //Class Employees : List<Employee> is not COM compatible

    //Class Implement the Class Interface
    [Guid("0836089b-7099-4c0d-be97-39a009d1a9ba"),
     ComVisible(true),
     ClassInterface(ClassInterfaceType.None),
     ComDefaultInterface(typeof(IPerson)),
     ComSourceInterfaces(typeof(IPersonEvents)),
     ProgId("MyCOMEvents01.Person")]
    public class Person : IPerson
    {
        [ComVisible(false)] //Does not need to be visible to COM
        public delegate void OnAfterNameChangeHandler(object sender, string name);

        [ComVisible(false)] //Does not need to be visible to COM
        public delegate void OnBeforeNameChangeHandler(object sender, string newName, ref bool cancel);

        public event OnAfterNameChangeHandler OnAfterNameChange;
        public event OnBeforeNameChangeHandler OnBeforeNameChange;

        public string Id { get; set; }

        private string _Name;
        public string Name
        {
            get { return _Name; }
            set
            {
                bool cancel = false;

                if (OnBeforeNameChange != null)
                {
                    //if we define a COM object WithEvents in VBA, OnPesonAdd will not be null even if we do not associate any code to it.
                    //So we must protect the code.
                    try { OnBeforeNameChange(this, value.ToString(), ref cancel);}
                    catch (Exception){} //Do Nothing
                }

                if (cancel == false)
                {
                    _Name = value;
                    if (OnAfterNameChange != null)
                    {
                        //if we define a COM object WithEvents in VBA, OnPesonAdd will not be null even if we do not associate any code to it.
                        //So we must protect the code.
                        try { OnAfterNameChange(this, _Name); }
                        catch (Exception){} //Do Nothing
                    }
                }
            }
        }

        public double Age { get; set; }

    }
}

You can find here a quick VBA code to test the classs


Option Explicit

Dim WithEvents p As MyCOMEvents01.Person


Sub test()
 Set p = New MyCOMEvents01.Person
 p.Name = "Mario"
 p.Name = "Ciccio"
End Sub


Private Sub p_OnAfterNameChange(ByVal sender As Variant, ByVal Name As String)
   Debug.Print Name
End Sub

Private Sub p_OnBeforeNameChange(ByVal sender As Variant, ByVal newName As String, cancel As Boolean)
  If newName = "Ciccio" Then
    cancel = True
    Debug.Print "Do not Change Name"
  End If
End Sub

No comments:

Post a Comment