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

跨AppDomain调用DLL触发事件时出现未处理空引用异常

问题分析与解决:跨AppDomain订阅DLL事件触发空引用异常

我的项目包含两个组件:

  • Windows Forms应用程序(WindowsFormsApplication1)
  • 类库DLL(ClassLibrary1)

需求是当DLL中触发事件时,Windows Forms应用执行对应逻辑,当前通过AppDomain访问DLL方法,但运行时抛出空引用异常。


Windows Forms应用Form1.cs代码

using System;
// more usings ...
using ClassLibrary1;
using System.Reflection;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            trial obj = new trial();
            obj.trial1();
         }
      }

    class trial 
    {
        void HandleEvent(object sender, EventArgs e)
        {
            Console.WriteLine("HandleEvent called");
        }

         AppDomain ad = AppDomain.CreateDomain("Test");
         string DLL = @"C:\..\ClassLibrary1.dll";
            
           public void trial1()
            {
                 trial pro = new trial();
                 MethodInfo method = typeof(trial).GetMethod("HandleEvent", BindingFlags.NonPublic | BindingFlags.Instance);

                // Subscribe to the event
                EventInfo eventInfo = typeof(ClassLibrary1.Class1).GetEvent("TestEvent");
                Type type = eventInfo.EventHandlerType;
                Delegate handler = Delegate.CreateDelegate(type, pro, method);
                // method call 1
                Loader.Call(ad, DLL,"ClassLibrary1.Class1","RaiseEvent",DateTime.Now.ToShortDateString());
            }
    }

    public class Loader : MarshalByRefObject
    {
        object CallInternal(string dll, string typename, string method, object[] parameters)
        {
            Assembly a = Assembly.LoadFile(dll);
            object o = a.CreateInstance(typename);
            Type t = o.GetType();
            MethodInfo m = t.GetMethod(method);
            return m.Invoke(o, parameters);
        }

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

ClassLibrary1.dll代码

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

namespace ClassLibrary1
{
    public class Class1
    {
        public event EventHandler TestEvent;

        public int RaiseEvent(string msg)
        {
            try
            {
                TestEvent(this, EventArgs.Empty);
            }
            catch (Exception ex)
            {
                Console.WriteLine("the exception is: " + ex.ToString());
                if (ex.InnerException != null)
                {
                    Console.WriteLine("the inner exception is: " + ex.InnerException.Message.ToString());
                }
            }
            return 2;
         }
    }
}

异常信息

Object reference not set to an instance of an object.

对比:控制台应用正常执行代码

using System;
using System.Reflection;

class Test
{
    void HandleEvent(object sender, EventArgs e)
    {
        Console.WriteLine("HandleEvent called");
    }

    static void Main()
    {
        Test test = new Test();
        ClassLibrary1.Class1 publisher = new ClassLibrary1.Class1();
        MethodInfo method = typeof(Test).GetMethod("HandleEvent", BindingFlags.NonPublic | BindingFlags.Instance);

        // Subscribe to the event
        EventInfo eventInfo = typeof(ClassLibrary1.Class1).GetEvent("TestEvent");
        Type type = eventInfo.EventHandlerType;
        Delegate handler = Delegate.CreateDelegate(type, test, method);

        eventInfo.AddEventHandler(publisher, handler);
        publisher.RaiseEvent("hello");
    }
}

问题根源

核心问题是跨AppDomain的事件订阅逻辑错误:

  1. WinForms代码中,你在主AppDomain创建了委托并尝试订阅事件,但实际调用RaiseEvent时,是在新建的Test AppDomain中创建了一个全新的Class1实例——两个实例分属不同AppDomain,主域的订阅完全没关联到实际触发事件的实例,导致TestEvent为null,触发空引用异常。
  2. 控制台程序没有使用多AppDomain,所有对象都在同一个域内,订阅和触发针对的是同一个实例,所以正常工作。

解决方案

方案一:在目标AppDomain内完成事件订阅与触发

修改代码,让事件订阅和触发在同一个AppDomain内完成,同时确保事件处理类可跨域访问:

  1. 修改trial类继承MarshalByRefObject(允许跨AppDomain传递代理):
class trial : MarshalByRefObject
{
    public void HandleEvent(object sender, EventArgs e)
    {
        // WinForms环境建议用MessageBox替代Console.WriteLine
        MessageBox.Show("HandleEvent called");
    }

    string DLL = @"C:\..\ClassLibrary1.dll";
        
    public void trial1()
    {
        AppDomain ad = AppDomain.CreateDomain("Test");
        // 将当前trial实例传递到目标AppDomain
        Loader.Call(ad, DLL, this);
        AppDomain.Unload(ad);
    }
}
  1. 修改Loader类,增加订阅事件的逻辑:
public class Loader : MarshalByRefObject
{
    public void SetupAndRaiseEvent(string dll, MarshalByRefObject eventHandler)
    {
        Assembly a = Assembly.LoadFile(dll);
        object o = a.CreateInstance("ClassLibrary1.Class1");
        Type t = o.GetType();

        // 在当前AppDomain内完成事件订阅
        EventInfo eventInfo = t.GetEvent("TestEvent");
        MethodInfo handlerMethod = eventHandler.GetType().GetMethod("HandleEvent", BindingFlags.Public | BindingFlags.Instance);
        Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, eventHandler, handlerMethod);
        eventInfo.AddEventHandler(o, handler);

        // 触发事件
        MethodInfo m = t.GetMethod("RaiseEvent");
        m.Invoke(o, new object[] { DateTime.Now.ToShortDateString() });
    }

    public static void Call(AppDomain domain, string dll, MarshalByRefObject eventHandler)
    {
        Loader ld = (Loader)domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
        ld.SetupAndRaiseEvent(dll, eventHandler);
    }
}

方案二:移除多AppDomain逻辑(无特殊隔离需求时推荐)

如果不需要AppDomain的隔离/卸载能力,直接在主AppDomain内实例化DLL对象,和控制台程序逻辑一致:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        trial obj = new trial();
        obj.trial1();
    }
}

class trial 
{
    void HandleEvent(object sender, EventArgs e)
    {
        MessageBox.Show("HandleEvent called");
    }

    public void trial1()
    {
        ClassLibrary1.Class1 publisher = new ClassLibrary1.Class1();
        MethodInfo method = typeof(trial).GetMethod("HandleEvent", BindingFlags.NonPublic | BindingFlags.Instance);
        EventInfo eventInfo = typeof(ClassLibrary1.Class1).GetEvent("TestEvent");
        Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, method);
        eventInfo.AddEventHandler(publisher, handler);
        publisher.RaiseEvent(DateTime.Now.ToShortDateString());
    }
}

内容的提问来源于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.10 08:30:59