Search code examples
c#com

Implement IDispatch with using ICustomQueryInterface. Set property - not working


Demonstration example:

Echo.cs:

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

namespace EchoProject
{
    [ComVisible(true)]
    [ProgId("EchoProject.Echo")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Echo : IDispatch, ICustomQueryInterface
    {
        private int lastDispId = 0;
        private Dictionary<int, string> dispIdNameMap = new Dictionary<int, string>();

        public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
        {
            ppv = IntPtr.Zero;
            if (typeof(IDispatch).GUID == iid)
            {
                ppv = Marshal.GetComInterfaceForObject(this, typeof(IDispatch), CustomQueryInterfaceMode.Ignore);
                return CustomQueryInterfaceResult.Handled;
            }
            return CustomQueryInterfaceResult.NotHandled;
        }

        public void GetTypeInfoCount(out uint pctinfo)
        {
            pctinfo = 0;
        }

        public void GetTypeInfo(uint iTInfo, int lcid, out IntPtr info)
        {
            info = IntPtr.Zero;
        }

        public void GetIDsOfNames(ref Guid iid, string[] names, int cNames, int lcid, int[] rgDispId)
        {
            for (int i = 0; i < cNames; i++ )
            {
                KeyValuePair<int, string> pair = dispIdNameMap.SingleOrDefault(p => p.Value == names[i]);
                if (pair.Key == 0)
                {
                    dispIdNameMap.Add(++lastDispId, names[i]);
                    rgDispId[i] = lastDispId;
                }
                else
                {
                    rgDispId[i] = pair.Key;
                }
            }
        }

        public void Invoke(int dispId, ref Guid riid, int lcid, System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, out object result, IntPtr pExcepInfo, IntPtr puArgErr)
        {
            string name;
            dispIdNameMap.TryGetValue(dispId, out name);
            result = name;
        }
    }
}

IDispatch.cs:

using System;
using System.Runtime.InteropServices;

namespace EchoProject
{
    [ComImport]
    [Guid("00020400-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IDispatch
    {
        void GetTypeInfoCount(out uint pctinfo);

        void GetTypeInfo(uint iTInfo, int lcid, out IntPtr info);

        void GetIDsOfNames(
            ref Guid iid,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)]
                string[] names,
            int cNames,
            int lcid,
            [Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)]
                int[] rgDispId);

        void Invoke(
            int dispId,
            ref Guid riid,
            int lcid,
            System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags,
            ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
            out object result,
            IntPtr pExcepInfo,
            IntPtr puArgErr);
    }
}

echo.js:

var echo = new ActiveXObject("EchoProject.Echo");
WScript.Echo("Begin debug");
WScript.Echo(echo.foo()); //foo
WScript.Echo(echo.bar); //bar
echo.baz = 1; //error

Call method "foo" - working. Get property "bar" - working. Set property "baz" - not working!!!

I tried to use .NET framework versions: 4.5; 4.5.1; 4.5.2 - set property not working

What's the problem?


Solution

  • You should change the Invoke definition like this:

    void Invoke(
                int dispId,
                ref Guid riid,
                int lcid,
                System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags,
                ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
                [Out, MarshalAs(UnmanagedType.LPArray)] object[] result,
                IntPtr pExcepInfo,
                IntPtr puArgErr);
    

    and use it like this:

    public void Invoke(int dispId, ref Guid riid, int lcid, System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, out object result, IntPtr pExcepInfo, IntPtr puArgErr)
    {
        string name;
        dispIdNameMap.TryGetValue(dispId, out name);
        if (result != null)
        {
            result[0] = name;
        }
    }