用动态界面C#的COM对象 [英] C# COM object with a dynamic interface

查看:450
本文介绍了用动态界面C#的COM对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想建立一个 COM可见 C#类,说 DynamicComponent ,这将提供一个动态界面通过COM。

I'd like to build a COM visible C# class, say DynamicComponent, that would provide a dynamic interface through COM.

这个内部类将保持代表的字典:

Internally this class would maintain a dictionary of delegates:

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

客户端code会有些VBA。

The client code would be some VBA.

下面是工作流我天真地想象:

Here is the workflow I naively imagine:

  • 从Excel中/ VBA编辑器的用户引用 TLB
  • 用户实例化一个新的 DynamicComponent (嗯,至少得使用Excel / VBA提供的存根)
  • 在Excel中/ VBA COM结构通过它的的IDispatch 接口
  • 查询组件
  • 的成分是否以DISP-ID映射如 [GetTheAnswer - > 1,添加 - > 2]
  • 用户可以受益于自动完成,并认为两种方法: GetTheAnswer 添加
  • 用户调用任何这些方法,如果它是静态定义
  • from the Excel/VBA editor the user references the TLB
  • the user instantiates a new DynamicComponent (well at least get a stub provided by Excel/VBA)
  • Excel/VBA COM infrastructure queries the component through its IDispatch interface
  • the component answers with a disp-ids map like ["GetTheAnswer" -> 1, "Add" -> 2]
  • the user can benefit from auto-completion and sees two methods: GetTheAnswer and Add
  • the user invokes any of these methods as if it was statically defined

我的第一个问题:有可能

如果没有:?为什么

如果是:如何

这是我所知道的有关COM,如果可能的话,在 的IDispatch COM接口是我最好的朋友。

From what I know about COM, if it's possible, the IDispatch COM interface is my best friend.

此外,从我的理解,在 ICustomQueryInterface 界面可以大大帮助了。

Moreover, from what I understand, the ICustomQueryInterface interface from .Net 4 could greatly help too.

但作为时下COM是不是真的尖端;)这是非常难找到像code样本资源

But as nowadays COM is not really cutting-edge ;) it's quite hard to find resources like code samples.

我发现这个有趣的例子:的https://clrinterop.$c$ cplex.com/releases/view/32350 它实现了使用COM聚集在 ICustomQueryInterface 接口

I've found this interesting sample: https://clrinterop.codeplex.com/releases/view/32350 which implements COM aggregation using the ICustomQueryInterface interface

但这不是动态的,基于静态定义的类和接口。

But it's not dynamic and based on statically defined types and interfaces.

任何帮助将是很大的AP preciated。

Any help would be greatly appreciated.

感谢。

推荐答案

暴露 IDispatchEx 将工作的JavaScript,但我不认为VBA以任何方式使用它。 AFAIK,VBA依赖于的IDispatch 为后期绑定。此外,C#动态是伟大的COM 的IDispatch 在.NET端基于对象的消费,而不是相反。出于某种原因(.NET设计决定?),动态属性和 ExpandoObject DynamicObject 不暴露在COM方法在默认情况下。

Exposing IDispatchEx would work for JavaScript, but I don't think VBA makes any use of it. AFAIK, VBA relies upon IDispatch for late binding. Furthermore, C# dynamic is great for consumption of COM IDispatch-based objects on .NET side, but not vice versa. For some reason (.NET designers decision?), dynamic properties and methods of ExpandoObject and DynamicObject are not exposed to COM by default.

幸运的是,有一种方法来覆盖这一点:通过实施 IReflect 接口。请参阅本优秀的博客帖子的实施细则。我有我自己看了在公开C#匿名类的属性COM ,并结束了使用 IReflect 。这是如何可以公开的动态方法和属性COM。形象地说, IReflect 向COM公开,如的IDispatch

Fortunately, there's a way to override this: by implementing IReflect interface. Refer to this excellent blog post for implementation details. I've myself looked at exposing properties of C# anonymous class to COM, and ended up using IReflect. This is how you can expose dynamic methods and properties to COM. Figuratively speaking, IReflect is exposed to COM as IDispatch.

在一个侧面说明,<一个href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.expando.iexpando.aspx">IExpando做相同的工作为 IDispatchEx ,这样一个JavaScript客户端可以添加新的特性以后可以通过管理code访问。

On a side note, IExpando does the same job for IDispatchEx, so a JavaScript client can add new properties which can later be accesses by managed code.

[更新] 下面是一个原型实现暴露 DynamicComponent 的实例为VBScript里面 web浏览器<运行/ code>。它工作得很好VBScript和应该这样做的VBA了。虽然,我怀疑VBA自动完成将工作,或者有实现这种功能的简便方法。 AFAIU,VBA自动完成依赖于COM类型库(获得通过的IDispatch :: GetTypeInfo的),但我不认为.NET互操作引擎将生成一个动态类型库时它实现的IDispatch IReflect (我可能是错的)。此外,这种实现是区分大小写的方法,按姓名查找窗口,应调整为VB是不区分大小写。

[UPDATE] Below is a prototype implementation that exposes an instance of DynamicComponent to VBScript running inside WebBrowser. It works quite well for VBScript and should do so for VBA too. Although, I doubt VBA auto-completion will work, or there is an easy way to implement such feature. AFAIU, VBA auto-completion relies upon COM type library (obtainable via IDispatch::GetTypeInfo), but I don't think .NET interop engine would generate a dynamic type library when it implements IDispatch through IReflect (I might be wrong). Also, this implementation is case-sensitive for method-by-name look-ups, which should be tweaked as VB is case-insensitive.

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
{
    // http://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
}

这篇关于用动态界面C#的COM对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆