【问题标题】:C# COM and attaching eventsC# COM 和附加事件
【发布时间】:2014-09-08 17:28:45
【问题描述】:

所以,我有这个遗留类型的应用程序,我正在努力理解和维护,需要一些帮助。 我还没有涉足 Remoting,但这个解决方案似乎使用了 3rd 方 COM 组件。

该文档是合理的,但没有过多谈论 .NET 以及如何附加事件处理程序。相反,它记录(并在解决方案中使用)如何通过 Activator.CreateInstance 创建对象的实例。

所以目前它正在这样做:

mainObj = Activator.CreateInstance(Type.GetTypeFromProgID("xxxRemote.clsxxxJob"));

没关系。

但我想附加一个事件处理程序来监听由 Activator 创建的实例化对象上的特定事件。

文档为我提供了暴露的事件,但没有说明在使用这种方法时如何将它们连接起来的示例。

在这样使用 COM 时,有人对如何连接事件有任何想法吗?

【问题讨论】:

  • 使用普通 .NET 对象的方法相同。糟糕的代码 sn-p,希望您将“mainObj”声明为 dynamic
  • 感谢@HansPassant - 不幸的是,这是一个 .NET 3.5 项目,我无法控制他们所做的事情或他们如何开发它,并且当您知道引入动态时,我无法升级到 .NET 4.0。无论如何,我如何准确地“订阅”激活器为该“对象”创建的事件?
  • 您必须使用反射或 VB.NET。 不要忘记这种人为的限制,卡在 6 年前的免费软件版本上并不常见。
  • 好吧,我不得不处理不幸的事情。那么使用 VB.NET 方法,如何做到这一点?该解决方案与 C# 和 VB.NET 混合在一起,并且都用各自的语言做同样的事情,但如果有意义的话,使用不同的“程序”
  • COM 和 Remoting 是两个不同的东西。

标签: c# events com


【解决方案1】:

它应该是通过正常反射。

msdn 中有一个指南,例如 http://msdn.microsoft.com/en-us/library/ms228976(v=vs.110).aspx

问题在于,要定义将与事件挂钩的方法,您不会定义类型,并且必须执行一些 IL 发出黑魔法来在运行时构建方法。这是一个有趣的问题。

我正在发送一个通过 COM 打开 ADO 连接的“正常”方式的示例(只是为了有一些会调用事件处理程序的东西)。然后,我有另一个这样做的例子,困难的方式。

对不起,代码中的混乱,但这只是一个示例。

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Reflection;
using ADODB;
using System.Reflection.Emit;

namespace COMTests.Tests
{
[TestClass]
public class UnitTest1
{
    private string server;
    private string database;
    private string user;
    private string password;                

    [TestInitialize]
    public void Initialize()
    {
        this.server = "";
        this.database = "";
        this.user = "";
        this.password = "";
    }

    [TestMethod]
    public void TestCreateConectionTheSaneWay()
    {
        ADODB.Connection connection = new ADODB.Connection();
        connection.Provider = "sqloledb";
        connection.ConnectionString = String.Format("Server={0};Database={1};User Id={2};Password={3}",
            this.server, this.database, this.user, this.password);
        connection.ConnectComplete += new ADODB.ConnectionEvents_ConnectCompleteEventHandler(TheConnectionComplete);
        connection.Open();


    }

    [TestMethod]
    public void TestCreateConnectionTheInsaneWay()
    {            
        Type connectionType = Type.GetTypeFromProgID("ADODB.Connection");

        EventInfo eventType = connectionType.GetEvent("ConnectComplete");            

        Type[] argumentTypes =
            (from ParameterInfo p in eventType.EventHandlerType.GetMethod("Invoke").GetParameters()
             select p.ParameterType).ToArray<Type>();

        MethodInfo handler = FabricateAMethod(argumentTypes, "Wow! Should I be happy because it works?", 
            "Ass2", "Type2", "Method2");            
        Delegate d2 = Delegate.CreateDelegate(eventType.EventHandlerType, handler, true);                       

        object o = Activator.CreateInstance(connectionType);
        eventType.AddEventHandler(o, d2);            

        connectionType.GetProperty("Provider").SetValue(o, "sqloledb", null);
        connectionType.GetProperty("ConnectionString").SetValue(o, String.Format("Server={0};Database={1};User Id={2};Password={3}",
            this.server, this.database, this.user, this.password), null);
        connectionType.GetMethod("Open").Invoke(o, new object[] { "", "", "", -1 });            

    }

    [TestMethod]
    public void TestFabricatedMethod()
    {    
        MethodInfo m = FabricateAMethod(new Type[] {}, "Yeap. Works.", "Ass1", "Type1", "Method1");
        m.Invoke(null, new Object[] { });
    }

    private MethodInfo FabricateAMethod(Type[] arguments, string stringToPrint, string assemblyName, 
        string typeName, string methodName)
    {
        AssemblyName aName = new AssemblyName(assemblyName);
        AssemblyBuilder ab =
            AppDomain.CurrentDomain.DefineDynamicAssembly(
                aName,
                AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder mb =
           ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

        TypeBuilder tb = mb.DefineType(
            typeName,
            TypeAttributes.Public);

        MethodBuilder method = tb.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Static,
           typeof(void), arguments);

        MethodInfo writeString = typeof(Console).GetMethod("WriteLine",
        new Type[] { typeof(string) });
        ILGenerator il = method.GetILGenerator();

        il.Emit(OpCodes.Ldstr, stringToPrint);
        il.EmitCall(OpCodes.Call, writeString, null);
        il.Emit(OpCodes.Ret);

        return tb.CreateType().GetMethod(methodName);
    }

    public static void TheConnectionComplete(Error pError, ref EventStatusEnum adStatus, Connection pConnection)
    {
        Console.WriteLine("The normal way.");
    }

}

}

【讨论】:

  • 发送一些 cmets,如果太难,我可以帮助您并编辑答案。示例编译。只需在“COM”选项卡中添加对“Microsoft ActiveX Data Objects 6.1”的引用。如果您不想添加引用,只需删除 TestCreateConnectionTheSaneWay 和其他 ADODB 类型。
猜你喜欢
  • 2013-02-18
  • 2016-04-29
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
  • 2016-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多