Subscribing to COM event from C# using late binding

0

I have download the source code the the Out-of-process COM server in C# from this MS site: https://code.msdn.microsoft.com/windowsapps/CSExeCOMServer-3b1c1054. In that code, there is a definition for a COM object named SimpleObject. The object is define in this way:

[Guid(SimpleObject.InterfaceId), ComVisible(true)]
public interface ISimpleObject
{
    float FloatProperty { get; set; }
    string HelloWorld();
}

Then where is an events interface:

[Guid(SimpleObject.EventsId), ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISimpleObjectEvents
{
    [DispId(1)]
    void FloatPropertyChanging(float NewValue, ref bool Cancel);
}

The component iself is defined like this:

[ClassInterface(ClassInterfaceType.None)] 
    [ComSourceInterfaces(typeof(ISimpleObjectEvents))]
    [Guid(SimpleObject.ClassId), ComVisible(true)]
    public class SimpleObject : ReferenceCountedObject, ISimpleObject
    {
        internal const string ClassId =
            "DB9935C1-19C5-4ed2-ADD2-9A57E19F53A3";
        internal const string InterfaceId =
            "941D219B-7601-4375-B68A-61E23A4C8425";
        internal const string EventsId =
            "014C067E-660D-4d20-9952-CD973CE50436";

        [EditorBrowsable(EditorBrowsableState.Never)]
        [ComRegisterFunction()]
        public static void Register(Type t)
        {
            // Registration bolderplate, ommited
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        [ComUnregisterFunction()]
        public static void Unregister(Type t)
        {
            // Registration bolderplate, ommited
        }

        private float fField = 0;
        public float FloatProperty
        {
            get { return this.fField; }
            set
            {
                bool cancel = false;
                if (null != FloatPropertyChanging)
                    FloatPropertyChanging(value, ref cancel);
                if (!cancel)
                    this.fField = value;
            }
        }
        public string HelloWorld()
        {
            return "HelloWorld";
        }
        [ComVisible(false)]
        public delegate void FloatPropertyChangingEventHandler(float NewValue, ref bool Cancel);
        public event FloatPropertyChangingEventHandler FloatPropertyChanging;
    }

At the end we have the class factory which is just boilerplate code.

internal class SimpleObjectClassFactory : IClassFactory
{
    public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, 
        out IntPtr ppvObject)
    {
        ppvObject = IntPtr.Zero;

        if (pUnkOuter != IntPtr.Zero)
        {
            // The pUnkOuter parameter was non-NULL and the object does 
            // not support aggregation.
            Marshal.ThrowExceptionForHR(COMNative.CLASS_E_NOAGGREGATION);
        }

        if (riid == new Guid(SimpleObject.ClassId) ||
            riid == new Guid(COMNative.IID_IDispatch) ||
            riid == new Guid(COMNative.IID_IUnknown))
        {
            // Create the instance of the .NET object
            ppvObject = Marshal.GetComInterfaceForObject(
                new SimpleObject(), typeof(ISimpleObject));
        }
        else
        {
            // The object that ppvObject points to does not support the 
            // interface identified by riid.
            Marshal.ThrowExceptionForHR(COMNative.E_NOINTERFACE);
        }

        return 0;   // S_OK
    }

    public int LockServer(bool fLock)
    {
        return 0;   // S_OK
    }
}

Now what I want to do, is to subscribe to the FloatPropertyChanging event using late binding.

I have tried doing this in the following way:

public void TestMethod3()
{
    var type = Type.GetTypeFromProgID("QuasarCOMServer.COMObjects.SimpleObject"); // adjusted namespace hence the name
    dynamic instance = Activator.CreateInstance(type);
    instance.FloatProperty = 10; 
    instance.FloatPropertyChanging += new Action<float>(InstanceOnFloatPropertyChanging); // <- this is the problem
}

private void InstanceOnFloatPropertyChanging(float newvalue)
{
    Debug.WriteLine("Value changed to " + newvalue);
}

Now the server goes up, I can see the process, and calling instace.FloatProperty works as expected.

The problem is subscribing to event; the event is of type System.Dynamic.BoundDispEvent. The source code for this types brings me here: https://searchcode.com/codesearch/view/52696999/. This shows, that this type contains a method Called TryBinaryOperation which seems to accept "AddAssign" type of expression. However, executing the line instance.FloatPropertyChanging += new Action<float>(InstanceOnFloatPropertyChanging); just returns

System.Runtime.InteropServices.COMException: 'Exception from HRESULT: 0x80040200'.

How do I do this properly?

c#
.net
com
asked on Stack Overflow Sep 16, 2018 by Andro

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0