Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 是否可以防止子AppDomains中未处理的异常导致主进程崩溃?_.net_Process_Crash_Appdomain_Unhandled Exception - Fatal编程技术网

.net 是否可以防止子AppDomains中未处理的异常导致主进程崩溃?

.net 是否可以防止子AppDomains中未处理的异常导致主进程崩溃?,.net,process,crash,appdomain,unhandled-exception,.net,Process,Crash,Appdomain,Unhandled Exception,我正在编写一个小插件库,它使用应用程序域来隔离使用.NETFramework4.0的插件。因此,每个插件中的代码都是我无法控制的。当一个插件中出现未经处理的异常时,我观察到结果有点复杂。具体如下 当插件的主线程中抛出未处理的异常时,调用插件的execute方法的主插件应用程序能够捕获并干净地处理它。没问题。但是, 如果插件在插件的Execute方法中为基于WinForms的应用程序启动一个消息循环,并且WinForm应用程序(即表单)中抛出一个未处理的异常,那么可插入应用程序只能在从visual

我正在编写一个小插件库,它使用应用程序域来隔离使用.NETFramework4.0的插件。因此,每个插件中的代码都是我无法控制的。当一个插件中出现未经处理的异常时,我观察到结果有点复杂。具体如下

当插件的主线程中抛出未处理的异常时,调用插件的execute方法的主插件应用程序能够捕获并干净地处理它。没问题。但是,

  • 如果插件在插件的Execute方法中为基于WinForms的应用程序启动一个消息循环,并且WinForm应用程序(即表单)中抛出一个未处理的异常,那么可插入应用程序只能在从visual studio调试器内部运行时捕获该异常。否则(在VS外部调用时),主可插拔应用程序将与插件一起崩溃

  • 如果未处理的异常被抛出到插件的Execute方法生成的单独线程中,那么可插拔应用程序就没有机会捕捉到异常,它就会崩溃

  • 我在下面的链接中创建了一个简单的VS2010项目来模拟这种行为。

    其中,可插拔应用程序中的主要方法如下所示

    namespace PluginExceptionTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Press enter to load plugin");
                Console.ReadLine();
    
                Assembly entryAsm = Assembly.GetEntryAssembly();
                string assemblyFileName = Path.Combine(Path.GetDirectoryName(entryAsm.Location), "EvilPlugin.exe");
    
    
                AppDomainSetup domainSetup = new AppDomainSetup();
                AppDomain domain = AppDomain.CreateDomain("PluginDomain", null, domainSetup);
                PluginBase plugin = (PluginBase)domain.CreateInstanceFromAndUnwrap(assemblyFileName, "EvilPlugin.Plugin");
    
                Console.WriteLine("Plugin Loaded.");
    
                //SCENARIO 1: WinForms based plugin
                Console.WriteLine("Press Enter to execute winforms plugin. (Remember to click on the button in the form to raise exception)");
                Console.ReadLine();
    
                try
                {
                    plugin.ExecuteWinApp();
                }
                catch (Exception)
                {
                    //The exception is caught and this gets executed only when running in visual studio debugger. Else application exits. Why?
                    Console.WriteLine("WinForms plugin exception caught. However same does not happen when run out of visual studio debugger. WHY?");
                }
    
                //SCENARIO 2: WinForms based plugin
                Console.WriteLine("Press Enter to execute threading plugin, wait for 3 seconds and the app will exit. How to prevent app from exiting due to this?");
                Console.ReadLine();
                try
                {
                    plugin.ExecuteThread();
                }
                catch (Exception)
                {
                    //This never gets executed as the exception is never caught. Application exits. Why?
                    Console.WriteLine("WinForms plugin exception caught");
                }
    
                Console.ReadLine();
            }
        }
    }
    
    这是插件项目的代码。它继承自上述可插拔应用程序项目中的PluginBase类

    namespace EvilPlugin
    {
        public class Plugin:PluginBase
        {
            public Plugin():base()
            {
    
            }
    
            public override void ExecuteWinApp()
            {            
                Application.Run(new Form1());            
            }
    
            public override void ExecuteThread()
            {
                Thread t = new Thread(new ThreadStart(RaiseEx));           
                t.Start();
            }
    
            private void RaiseEx()
            {
                Thread.Sleep(3000);
                throw new Exception("Another Evil Exception in a seperate thread");
            }
        }
    }
    
    最后,这是插件中表单的代码

    namespace EvilPlugin
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnException_Click(object sender, EventArgs e)
            {
                throw new Exception("Evil Exception");
            }
        }
    }
    
    如何防止主进程因上述两种情况(1和2)而退出


    提前感谢。

    处理WinForms异常。您可以在PluginBase类中为此类线程异常设置“陷阱”:

    public abstract class PluginBase:MarshalByRefObject
    {
        protected PluginBase()
        {
            System.Windows.Forms.Application.ThreadException +=
                (o, args) =>
                {
                    throw new Exception("UntrappedThread Exception:" + args.Exception);
                };
    
        }
    
        public abstract void ExecuteWinApp();
        public abstract void ExecuteThread();
    }
    
    默认情况下,它将显示“异常窗口”。但是如果提供的话,它将到达这个处理程序

    至于从其他线程捕获异常。没有办法。您所能做的最好的事情就是有一个关于抛出异常的通知

        AppDomain domain = AppDomain.CreateDomain("PluginDomain", null, domainSetup);
        domain.UnhandledException +=
            (o, eventArgs) =>
                {
                    Console.WriteLine("Exception was caught from other AppDomain: " + eventArgs.ExceptionObject);
                    Console.WriteLine("CLR is terminating?: " + eventArgs.IsTerminating);
                };
    

    因此,AppDomain方法似乎不适合插件库,因为我不希望我的主应用程序因为插件在它生成的线程中导致异常而崩溃。我想我还是回到了把它包装在流程中的老方法吧。当msdn说AppDomain提供了一种隔离程序集的方法时,它不是100%正确的。AppDomain隔离数据,不是执行。我想知道是否有某种方法可以防止新的AppDomain像上面的ExecuteThread方法那样产生额外的线程……说真的,如果子AppDomain仅限于一个线程,我也会对这种解决方案感兴趣。