带有AddeventHandler的动态加载DLL

本文关键字:加载 DLL 动态 AddeventHandler 带有 | 更新日期: 2023-09-27 17:59:16

我浏览了stackoverflow和msdn的许多帖子,并尝试了他们的解决方案,但我仍然无法实现,所以我希望任何人都能帮助我。非常感谢

首先,我还没有添加

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

直接呼叫

 ei.AddEventHandler(instance, handler);

则错误为

"xxxxErrorEventHandler"类型的对象无法转换为类型'xxxxErrorEventHandler'。

然后我按照下面的帖子修改我的代码,然后在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!");
    }
}

回复楼层1

嗨,汉斯,非常感谢你的回答,我听从你的建议修改我的代码。如果回调函数像这样声明

private static void Callback(string msg)

并且在ClassA事件内部也像这样声明

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

这很好,但如果像在之前一样声明

private static void Callback(object sender, ErrorEventArgs data)

当运行时,它会出现错误"无法绑定到目标方法,因为它的签名或安全透明性与委托类型不兼容。"

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

你知道为什么吗?顺便说一句,真的非常感谢你的帮助。

如果EXE端的修改代码类似于,则出错

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

带有AddeventHandler的动态加载DLL

您的程序有两个委托类型,名为ErrorEventHandler。一个在上部代码段中声明,另一个在下部代码段中宣布。它们有不同的名称,甚至可能不在同一个命名空间中,但这实际上并不重要。

CLR断然拒绝认为两个委托类型相同,即使它们的声明相同。它不会消耗额外的CPU周期来使用反射本身来检查它们是否匹配,代理应该很快,而且检查兼容性没有什么便宜的。类型转换是编译器的工作。因此,它只接受完全匹配,您的反射代码需要创建一个ClassA.ErrorEventHandler类型的委托。当然,您不知道类型,但它很容易获得。它是EventInfo.EventHandlerType.

代码中的第二个问题是委托目标,即引发事件时运行的方法。现在您通过了ClassA.Run(),当然这是不正确的。您需要传递自己的方法,一个与委托兼容的方法。它可能看起来像这样:

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

并相应地修改反射代码:

        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);

请注意,回调事件处理程序使用对象作为第二个参数类型。这很好,它与不可见的ErrorEventArgs类型兼容。如果需要获得传递对象的值,那么也需要在回调中使用反射。