Dynamic Load DLL with AddeventHandler

2019-09-02 10:29发布

I have walkthru much post of stackoverflow and msdn and tried their solution , but I still cannot get it works, so I hope anyone can help me out . Thank you very much

First I havent added

MethodInfo mi = instance.GetType().GetMethod(sMethodName, myBindingFlags);
Delegate del = Delegate.CreateDelegate(typeof(ErrorEventHandler), null, mi);

directly call

 ei.AddEventHandler(instance, handler);

then the error is

Object of type 'xxxxErrorEventHandler' cannot be converted to type 'xxxxErrorEventHandler'.

Then I follow some post modify my code as below, then raise the error when CreateDelegate

DLL:

public class ErrorEventArgs : EventArgs
{
    public string ErrorMsg;
}

public interface IClassA
{
    bool Run();        
}

public class ClassA : IClassA
{     

    public delegate void ErrorEventHandler(object sender, ErrorEventArgs data);
    public event ErrorEventHandler OnErrorHandler;
    public void OnError(object sender, ErrorEventArgs data)
    {
        if (OnErrorHandler != null)
        {
            OnErrorHandler(this, data);
        }
    }

    public bool Run()
    {
         // do something inside DLL
         ErrorEventArgs data = new ErrorEventArgs();
         data.ErrorMsg = "Hello World";
         OnError(this, data)
    }
}

EXE:

public delegate void ErrorEventHandler(object sender, ErrorEventArgs data);

void main()
{
    Assembly assembly = Assembly.LoadFile("myDLL.dll");
    Type[] types = assembly.GetTypes();
    for (int i = 0; i < types.Length; i++)
    {
        Type type = assembly.GetType(types[i].FullName);
        if (type.GetInterface("myDLL.IClassA") != null)
        {
             object obj = Activator.CreateInstance(type);
             if (obj != null)
             {
                 MethodInfo methodInfo = obj.GetType().GetMethod("Run");

                 ErrorEventHandler handler = foo;   
                 BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;
                 EventInfo ei = instance.GetType().GetEvent("OnErrorHandler", myBindingFlags);
                 MethodInfo mi = instance.GetType().GetMethod("Run", myBindingFlags);

                 *// System.ArgumentException raised here
                 //  Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.*
                 Delegate del = Delegate.CreateDelegate(typeof(ErrorEventHandler), null, mi);
                 ei.AddEventHandler(instance, del);

                 bool Result = methodInfo.Invoke(instance, null);
             }
        }                            
    }


    public void foo(object sender, ErrorEventArgs data)
    {
        Console.WriteLine("In Foo!");
    }
}

reply to floor 1

Hi Hans, Thanks a lot for your answer, I following your advise to modify my code. If the Callback function declare like this

private static void Callback(string msg)

and inside ClassA event also declare like this

public delegate void ErrorEventHandler(string msg);
public event ErrorEventHandler OnErrorHandler;
public void OnError1(string msg)
{
    if (OnErrorHandler != null)
    {
        OnErrorHandler(msg);
    }
}

that works fine, but if declare it like before

private static void Callback(object sender, ErrorEventArgs data)

it comes up error " Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type." when run

Delegate del = Delegate.CreateDelegate(ei.EventHandlerType, null, mycallback);

Do you know why , by the way really really thanks for your help.

error if the modified code at EXE side like:

      .......
      BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;
      EventInfo ei = instance.GetType().GetEvent("OnErrorHandler", myBindingFlags);
      MethodInfo mi = instance.GetType().GetMethod(sMethodName, myBindingFlags);
      var mycallback = typeof(ModuleManager).GetMethod("Callback", BindingFlags.Static | BindingFlags.NonPublic);
      Delegate del = Delegate.CreateDelegate(ei.EventHandlerType, null, mycallback);
      ei.AddEventHandler(instance, del);
      .......



 private static void Callback(object sender, ErrorEventArgs data)
 {
       Console.WriteLine("In Callback!");
 }

标签: c# dll delegates
1条回答
beautiful°
2楼-- · 2019-09-02 10:49

Your program has two delegate types named ErrorEventHandler. One declared in the upper snippet, another declared in the lower snippet. They have distinct names and probably don't even live in the same namespace, but that doesn't actually matter.

The CLR categorically refuses to consider two delegate types identical, even if their declaration is the same. It will not burn the extra CPU cycles to use reflection itself to check if they match, delegates are supposed to be fast and there's nothing cheap about checking compatibility. Type conversion is a compiler's job. So it will only accept an exact match, your reflection code needs to create a delegate of type ClassA.ErrorEventHandler. Of course you can't know the type, it is readily available however. It is EventInfo.EventHandlerType.

A second problem in your code is the delegate target, the method that runs when the event is raised. You now pass ClassA.Run(), that is not correct of course. You need to pass your own method, one that's compatible with the delegate. It could look like this:

    private static void Callback(object sender, object data) {
        // etc...
    }

And modify the reflection code accordingly:

        object obj = Activator.CreateInstance(type);
        BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;

        // Find the event and its type
        EventInfo ei = obj.GetType().GetEvent("OnErrorHandler", myBindingFlags);
        var delegateType = ei.EventHandlerType;

        // Use our own event handler
        var mycallback = typeof(Program).GetMethod("Callback", BindingFlags.Static | BindingFlags.NonPublic);
        Delegate del = Delegate.CreateDelegate(delegateType, null, mycallback);
        ei.AddEventHandler(obj, del);

        // Call the Run method
        MethodInfo methodInfo = obj.GetType().GetMethod("Run");
        bool Result = (bool)methodInfo.Invoke(obj, null);

Note that the Callback event handler used object as the second argument type. Which is fine, it is compatible with the invisible ErrorEventArgs type. If you need to obtain the values of the passed object then you need to use Reflection in the callback as well.

查看更多
登录 后发表回答