3

COM を介して動的インターフェイスを提供するCOM 可視C# クラス、たとえばを構築したいと考えています。DynamicComponent

内部的に、このクラスはデリゲートのディクショナリを維持します。

"GetTheAnswer" -> () => { return 42; }
"Add" -> (int a, int b) => { return a + b; }
...

クライアント コードは VBA になります。

これが私が素朴に想像するワークフローです。

  • ユーザーがTLBを参照する Excel/VBA エディタから
  • ユーザーが新しいインスタンスを作成しますDynamicComponent(少なくとも、Excel/VBA によって提供されるスタブを取得します)
  • Excel/VBA COM インフラストラクチャは、 IDispatchインターフェイスを介してコンポーネントにクエリを実行します
  • コンポーネントは次のような disp-ids マップで応答します["GetTheAnswer" -> 1, "Add" -> 2]
  • ユーザーはオートコンプリートの恩恵を受けることができ、次の 2 つの方法が表示されますGetTheAnswerAdd
  • ユーザーは、静的に定義されているかのように、これらのメソッドのいずれかを呼び出します

私の最初の質問:それは可能ですか?

いいえの場合:なぜですか?

はいの場合:どのように?

私が COM について知っていることから、可能であれば、IDispatch COM インターフェイスは私の親友です。

さらに、私が理解していることから、.Net 4のICustomQueryInterfaceインターフェイスも大いに役立ちます。

しかし、最近の COM は最先端ではありません ;) コード サンプルのようなリソースを見つけるのは非常に困難です。

この興味深いサンプルを見つけました: https://clrinterop.codeplex.com/releases/view/32350 ICustomQueryInterfaceインターフェイスを使用して COM 集約を実装します。

ただし、動的ではなく、静的に定義された型とインターフェイスに基づいています。

どんな助けでも大歓迎です。

ありがとう。

4

2 に答える 2

5

エクスポーズIDispatchExは JavaScript でうまくいきますが、VBA がそれを利用しているとは思いません。私の知る限り、VBAは遅延バインディングに依存していますIDispatch。さらに、C#dynamicは .NET 側で COMIDispatchベースのオブジェクトを使用するのに最適ですが、その逆はありません。何らかの理由で (.NET 設計者の決定?)、 および の動的プロパティとメソッドはExpandoObjectDynamicObject既定では COM に公開されません。

幸いなことに、これをオーバーライドする方法があります: IReflectインターフェイスを実装することです。実装の詳細については、この優れたブログ投稿を参照してください。私自身、C# の匿名クラスのプロパティを COM に公開することを検討し、最終的に を使用しIReflectました。これは、動的なメソッドとプロパティを COM に公開する方法です。比喩的に言えば、IReflectとして COM に公開されIDispatchます。

余談ですが、IExpandoは に対して同じジョブをIDispatchEx実行するため、JavaScript クライアントは、後でマネージ コードからアクセスできる新しいプロパティを追加できます。

[更新]DynamicComponent以下は、内部で実行されている VBScript にのインスタンスを公開するプロトタイプの実装ですWebBrowser。これは VBScript で非常にうまく機能し、VBA でも同様に機能するはずです。ただし、VBA のオートコンプリートが機能するかどうか、またはそのような機能を実装する簡単な方法があるとは思えません。AFAIU、VBA オートコンプリートは COM タイプ ライブラリ (経由で取得可能) に依存してIDispatch::GetTypeInfoいますが、.NET 相互運用エンジンが実装時に動的タイプ ライブラリを生成するとは思いません(間違っている可能性があります) IDispatchIReflectまた、この実装ではメソッド名による検索で大文字と小文字が区別されますが、VB では大文字と小文字が区別されないため、調整する必要があります。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WebBrowserApp
{
    // https://stackoverflow.com/a/19067386/1768303

    public partial class MainForm : Form
    {
        WebBrowser wb;

        public MainForm()
        {
            InitializeComponent();

            this.wb = new WebBrowser();
            this.wb.Dock = DockStyle.Fill;
            this.Controls.Add(this.wb);
            this.wb.Visible = true;

            var dynamicComponent = new DynamicComponent();
            // make dynamicComponent available to VBScript
            this.wb.ObjectForScripting = dynamicComponent;

            // add a dynamic method "Convert"
            dynamicComponent.SetMethod("Convert", new Func<int, string>((a) =>
            {
                MessageBox.Show("Convert called: " + a.ToString());
                return a.ToString();
            }));

            this.Load += (s, e) =>
            {
                this.wb.DocumentText =
                    "<script type='text/vbscript'>\n" +
                    "Sub OnLoadHandler\n" +
                    "    alert window.external.Convert(42)\n" +
                    "End Sub\n" +
                    "window.onload = GetRef(\"OnLoadHandler\")\n" +
                    "</script>";
            };
        }
    }

    #region DynamicComponent
    [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
    public class DynamicComponent : System.Reflection.IReflect
    {
        readonly Dictionary<string, Delegate> _methods = new Dictionary<string, Delegate>();

        public void SetMethod(string name, Delegate value)
        {
            _methods[name] = value;
        }

        static Exception NotImplemented()
        {
            var method = new StackTrace(true).GetFrame(1).GetMethod().Name;
            Debug.Assert(false, method);
            return new NotImplementedException(method);
        }

        #region IReflect
        // IReflect

        public FieldInfo GetField(string name, BindingFlags bindingAttr)
        {
            throw NotImplemented();
        }

        public FieldInfo[] GetFields(BindingFlags bindingAttr)
        {
            return new FieldInfo[0];
        }

        public MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
        {
            throw NotImplemented();
        }

        public MemberInfo[] GetMembers(BindingFlags bindingAttr)
        {
            return new MemberInfo[0];
        }

        public MethodInfo GetMethod(string name, BindingFlags bindingAttr)
        {
            throw NotImplemented();
        }

        public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
        {
            throw NotImplemented();
        }

        public MethodInfo[] GetMethods(BindingFlags bindingAttr)
        {
            return _methods.Keys.Select(name => new DynamicMethodInfo(name, _methods[name].Method)).ToArray();
        }

        public PropertyInfo[] GetProperties(BindingFlags bindingAttr)
        {
            return new PropertyInfo[0];
        }

        public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
        {
            throw NotImplemented();
        }

        public PropertyInfo GetProperty(string name, BindingFlags bindingAttr)
        {
            throw NotImplemented();
        }

        public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters)
        {
            if (target == this && invokeAttr.HasFlag(BindingFlags.InvokeMethod))
            {
                Delegate method;
                if (!_methods.TryGetValue(name, out method))
                    throw new MissingMethodException();
                return method.DynamicInvoke(args);
            }
            throw new ArgumentException();
        }

        public Type UnderlyingSystemType
        {
            get { throw NotImplemented(); }
        }
        #endregion

        #region DynamicMethodInfo
        // DynamicPropertyInfo

        class DynamicMethodInfo : System.Reflection.MethodInfo
        {
            string _name;
            MethodInfo _mi;

            public DynamicMethodInfo(string name, MethodInfo mi)
                : base()
            {
                _name = name;
                _mi = mi;
            }

            public override MethodInfo GetBaseDefinition()
            {
                return _mi.GetBaseDefinition();
            }

            public override ICustomAttributeProvider ReturnTypeCustomAttributes
            {
                get { return _mi.ReturnTypeCustomAttributes; }
            }

            public override MethodAttributes Attributes
            {
                get { return _mi.Attributes; }
            }

            public override MethodImplAttributes GetMethodImplementationFlags()
            {
                return _mi.GetMethodImplementationFlags();
            }

            public override ParameterInfo[] GetParameters()
            {
                return _mi.GetParameters();
            }

            public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture)
            {
                return _mi.Invoke(obj, invokeAttr, binder, parameters, culture);
            }

            public override RuntimeMethodHandle MethodHandle
            {
                get { return _mi.MethodHandle; }
            }

            public override Type DeclaringType
            {
                get { return _mi.DeclaringType; }
            }

            public override object[] GetCustomAttributes(Type attributeType, bool inherit)
            {
                return _mi.GetCustomAttributes(attributeType, inherit);
            }

            public override object[] GetCustomAttributes(bool inherit)
            {
                return _mi.GetCustomAttributes(inherit);
            }

            public override bool IsDefined(Type attributeType, bool inherit)
            {
                return _mi.IsDefined(attributeType, inherit);
            }

            public override string Name
            {
                get { return _name; }
            }

            public override Type ReflectedType
            {
                get { return _mi.ReflectedType; }
            }
        }

        #endregion
    }
    #endregion
}
于 2013-09-28T13:15:03.663 に答える