You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何捕获通过反射订阅的DLL事件返回的参数?

如何在跨AppDomain的WinForm事件处理程序中捕获事件返回参数

我有一个使用AppDomain加载DLL的WinForm应用(WindowsFormsApplication2),已订阅ClassLibrary.dll中实现的TestEvent事件。现在需要在WinForm的事件处理程序HandleEvent中捕获该事件的返回参数,具体实现方案如下:

原始代码参考

WinForm应用代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;
using ClassLibrary1;
using static System.Net.Mime.MediaTypeNames;

namespace WindowsFormsApplication2
{
  
    [Serializable]
    public partial class Form1 : Form
    {
        void HandleEvent(object sender, EventArgs e)
        {
            Debug.WriteLine("HandleEvent called");
        }
        string DLL = @"..\ConsoleApplication1\ClassLibrary1\bin\Debug\ClassLibrary1.dll";

        public Form1()
        {
            InitializeComponent();
            Loader.Call( DLL, "ClassLibrary1.Class1", "RaiseEvent", HandleEvent, DateTime.Now.ToShortDateString());

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Application.Restart();
            Application.Run(new Form1());
            this.Close();
        }
     
    }

    public class Loader : MarshalByRefObject
    {
        AppDomain ad = AppDomain.CreateDomain("Test");
        object CallInternal(string dll, string typename, string method, EventHandler handler, object[] parameters)
        {
            Assembly a = Assembly.LoadFile(dll);
            object o = a.CreateInstance(typename);
            Type t = o.GetType();

            // 订阅事件
            EventInfo eventInfo = t.GetEvent("TestEvent");
            eventInfo.AddEventHandler(o, handler);

            MethodInfo m = t.GetMethod(method);
            return m.Invoke(o, parameters);
        }

        public static object Call( string dll, string typename, string method, EventHandler handler, params object[] parameters)
        {
            Loader ld = (Loader)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
            object result = ld.CallInternal(dll, typename, method, handler, parameters);
            AppDomain.Unload(ad);
            return result;
        }
    }
}

ClassLibrary1.dll代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClassLibrary1
{
    [Serializable]
    public class Class1
    {
        public event EventHandler TestEvent;

        public int RaiseEvent(string msg)
        {
            try
            {
                TestEvent(this, EventArgs.Empty);
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常信息: " + ex.ToString());
                if (ex.InnerException != null)
                {
                    Console.WriteLine("内部异常信息: " + ex.InnerException.Message.ToString());
                }
            }
            return 2;
        }

    }
}

实现方案

要捕获事件的返回参数,核心是通过自定义可序列化的事件参数类传递数据——标准EventHandler的EventArgs无法携带自定义数据,且跨AppDomain传递对象必须支持序列化。具体步骤如下:

1. 定义自定义事件参数类

创建继承自EventArgs的类,标记[Serializable]以支持跨AppDomain序列化:

[Serializable]
public class TestEventArgs : EventArgs
{
    // 定义需要传递的返回参数
    public int ReturnValue { get; set; }
}

2. 修改ClassLibrary1中的事件定义与触发逻辑

将事件类型改为EventHandler<TestEventArgs>,触发事件时传入携带返回参数的自定义实例:

using System;

namespace ClassLibrary1
{
    [Serializable]
    public class Class1
    {
        // 替换原EventHandler为泛型EventHandler<TestEventArgs>
        public event EventHandler<TestEventArgs> TestEvent;

        public int RaiseEvent(string msg)
        {
            try
            {
                int result = 2; // 示例返回值,可替换为实际业务逻辑结果
                // 触发事件时传入自定义参数
                TestEvent?.Invoke(this, new TestEventArgs { ReturnValue = result });
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常信息: " + ex.ToString());
                if (ex.InnerException != null)
                {
                    Console.WriteLine("内部异常信息: " + ex.InnerException.Message.ToString());
                }
            }
            return 2;
        }
    }
}

3. 修改WinForm中的事件处理与订阅逻辑

更新事件处理方法的参数类型为TestEventArgs,并调整Loader类的委托类型以匹配新事件:

using System;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    [Serializable]
    public partial class Form1 : Form
    {
        // 修改事件处理方法参数为自定义TestEventArgs
        void HandleEvent(object sender, TestEventArgs e)
        {
            Debug.WriteLine("HandleEvent called");
            // 捕获并使用事件传递的返回参数
            Debug.WriteLine($"事件返回参数值: {e.ReturnValue}");
        }

        string DLL = @"..\ConsoleApplication1\ClassLibrary1\bin\Debug\ClassLibrary1.dll";

        public Form1()
        {
            InitializeComponent();
            // 订阅事件时使用泛型EventHandler<TestEventArgs>委托
            Loader.Call(DLL, "ClassLibrary1.Class1", "RaiseEvent", new EventHandler<TestEventArgs>(HandleEvent), DateTime.Now.ToShortDateString());
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Application.Restart();
            this.Close();
        }
    }

    public class Loader : MarshalByRefObject
    {
        static AppDomain ad = AppDomain.CreateDomain("Test");

        // 将参数中的EventHandler改为Delegate,适配泛型事件委托
        object CallInternal(string dll, string typename, string method, Delegate handler, object[] parameters)
        {
            Assembly a = Assembly.LoadFile(dll);
            object o = a.CreateInstance(typename);
            Type t = o.GetType();

            // 获取事件信息并订阅
            EventInfo eventInfo = t.GetEvent("TestEvent");
            eventInfo.AddEventHandler(o, handler);

            MethodInfo m = t.GetMethod(method);
            return m.Invoke(o, parameters);
        }

        public static object Call(string dll, string typename, string method, Delegate handler, params object[] parameters)
        {
            Loader ld = (Loader)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
            object result = ld.CallInternal(dll, typename, method, handler, parameters);
            AppDomain.Unload(ad);
            return result;
        }
    }

    // 自定义可序列化事件参数类,需确保ClassLibrary1也能访问该类型
    // 最优方案是将该类放在独立类库中,让两个项目都引用;也可复制代码到ClassLibrary1项目中
    [Serializable]
    public class TestEventArgs : EventArgs
    {
        public int ReturnValue { get; set; }
    }
}

关键注意事项

  • 序列化要求:自定义TestEventArgs必须标记[Serializable],否则跨AppDomain传递时会抛出序列化异常。
  • 类型一致性:确保TestEventArgs类型在WinForm和ClassLibrary1中完全一致(命名空间、类名、属性都相同),最稳妥的方式是将该类放在独立的类库项目中,让两个项目都引用。
  • 委托类型适配:Loader类的CallInternal方法参数改用Delegate而非EventHandler,以适配泛型事件委托类型。

内容的提问来源于stack exchange,提问作者Crystal Crystal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 13:50:16