C# COM and attaching events

2019-06-14 19:28发布

问题:

So, I have this legacy type app I am trying to understand and maintain and need some help. I have not dabbled much into Remoting but this solution appears to be using 3rd party COM components.

The documentation is reasonable but does not talk much about .NET and how to attach event handlers. Instead it documents (and is used in the solution) about how to create an instance of the object via Activator.CreateInstance.

so currently it is doing this:

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

that's fine.

But I want to attach an event handler to listen to a specific event on that instantiated object created by the Activator.

The documentation gives me the events exposed but no examples on how to go about wiring them up when using this approach.

anyone have any ideas on how to wire up events when using COM like this?

回答1:

it should be through normal reflection.

There's a guide in msdn like http://msdn.microsoft.com/en-us/library/ms228976(v=vs.110).aspx

The problem is that to define the methods that will be hooked to the events, you won't have the types defined and will have to do some IL emmiting black magic to build the methods in runtime. It's an interesting problem.

I'm sending an example of the "normal" way opening an ADO connection through COM (just to have something that will call an event handler). Then, I have another example of doing this, the hard way.

Sorry by the mess in the code, but it is just a sample.

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.");
    }

}

}



标签: c# events com