.net - traduccion - unhandled exception solucion
¿Pueden las excepciones no manejadas en Child AppDomains evitar que se bloquee el proceso principal? (1)
Estoy escribiendo una pequeña biblioteca de complementos que usa dominios de aplicaciones para aislar plugins usando .Net framework 4.0. Por lo tanto, el código que va en cada complemento está fuera de mi control. Cuando una excepción no controlada se plantea en uno de los complementos, he observado que los resultados son una especie de mezcla. Ellos son los siguientes.
Cuando se lanza la excepción no controlada en el hilo principal del complemento, la aplicación principal conectable que llama al método de ejecución del complemento puede capturarla y manejarla limpiamente. No hay problemas allí. Sin embargo,
Si el complemento inicia un bucle de mensaje para una aplicación basada en WinForms en el método Execute del complemento y se lanza una excepción no controlada a la aplicación WinForm (es decir, en un formulario), la aplicación conectable solo puede detectar la excepción si se ejecuta desde el depurador visual studio . De lo contrario (cuando se invoca fuera de VS), la aplicación principal conectable se bloquea junto con los complementos.
Si la excepción no controlada se genera en un hilo separado generado por el método Execute del complemento, entonces la aplicación conectable no tiene posibilidad de detectar la excepción y se bloquea.
Creé un proyecto simple VS 2010 para simular este comportamiento en el siguiente enlace. http://www.mediafire.com/file/1af3q7tzl68cx1p/PluginExceptionTest.zip
En él, el método principal en la aplicación conectable se parece a esto
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();
}
}
}
Este es el código para el proyecto de complemento. Hereda de la clase PluginBase en el proyecto de aplicación conectable anterior.
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");
}
}
}
Finalmente este es el código del Formulario en el complemento
namespace EvilPlugin
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnException_Click(object sender, EventArgs e)
{
throw new Exception("Evil Exception");
}
}
}
¿Cómo puedo evitar que el proceso principal salga debido a los dos escenarios (1 y 2) mencionados anteriormente?
Gracias por adelantado.
Para manejar la excepción de WinForms. Puede establecer "trap" para tales ThreadExceptions en la clase 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();
}
Como por defecto, le mostrará "La ventana de excepción". Pero llegará a este controlador si se proporciona.
En cuanto a atrapar la excepción de otro hilo. No hay forma. Lo mejor que puede hacer es recibir una notificación acerca de la excepción que se lanza.
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);
};