是否可以防止子 AppDomain 中的未处理异常导致主进程崩溃?
我正在编写一个小型插件库,它使用应用程序域来隔离使用 .Net Framework 4.0 的插件。因此,每个插件中的代码超出了我的控制范围。当其中一个插件中引发未处理的异常时,我观察到结果是好坏参半。它们如下。
当插件的主线程中抛出未处理的异常时,调用插件的执行方法的主可插拔应用程序能够干净地捕获并处理它。那里没有问题。但是,
如果插件在插件的 Execute 方法中为基于 WinForms 的应用程序启动消息循环,并且在 WinForm 应用程序(即表单中)中抛出未处理的异常,则可插入应用程序只能在运行时捕获异常从 Visual Studio 调试器内部。否则(在 VS 外部调用时),主可插入应用程序将与插件一起崩溃。
如果未处理的异常是在插件的 Execute 方法生成的单独线程中抛出的,则可插入应用程序没有机会捕获异常并且会崩溃。
我在下面的链接中创建了一个简单的 VS 2010 项目来模拟这种行为。 http://www.mediafire.com/file/1af3q7tzl68cx1p/PluginExceptionTest.zip
其中可插入应用程序中的主要方法如下所示
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)而退出?
提前致谢。
I'm writing a small plugins library which uses app domains to isolate plugins using .Net framework 4.0. Hence the code that goes in each plugin is out of my control. When an unhandled exception is raised in one of the plugins, I have observed the results to be kind of a mixed bag. They are as follows.
When the unhandled exception gets thrown in the plugin's main thread, the main pluggable app calling the plugin's execute method is able to catch and handle it cleanly. No problems there. However,
If the plugin starts a message loop for a WinForms based app in the plugin's Execute method and an unhandled exception gets thrown in the WinForm application (i.e. in a form) then the pluggable app can only catch the exception if its running from inside visual studio debugger. Otherwise (when invoked outside VS) the main pluggable app crashes along with the plugins.
If the unhandled exception is thrown in a separate thread spawned by the plugin's Execute method, then the pluggable app has no chance of catching the exception and it crashes.
I have created a simple VS 2010 project to simulate this behaviour at the link below.
http://www.mediafire.com/file/1af3q7tzl68cx1p/PluginExceptionTest.zip
In it the main method in the pluggable app looks like this
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();
}
}
}
This is the code for the plugin project. It inherits from the PluginBase class in the pluggable app project above.
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");
}
}
}
Finally this is the code from the Form in the plugin
namespace EvilPlugin
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnException_Click(object sender, EventArgs e)
{
throw new Exception("Evil Exception");
}
}
}
How can i prevent the main process from exiting due to the two scenarios (1 and 2) mentioned above?
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
用于处理 WinForms 异常。您可以在 PluginBase 类中为此类 ThreadException 设置“陷阱”:
默认情况下,它将显示“异常窗口”。但如果提供的话,将到达此处理程序。
至于从其他线程捕获异常。没有办法。您能做的最好的事情就是收到有关抛出异常的通知。
For handling WinForms exception. You could set "trap" for such ThreadExceptions in PluginBase class:
As by default, it will show you "The Exception Window". But will got to this handler if provided.
As for catching exception from other thread. There is no way. The best you could do is have a notification about Exception being thrown.