2010-08-29 7 views
6

¿Cómo atrapo un StackOverflowException?Captura de StackOverflowException

Tengo un programa que permite al usuario escribir scripts, y cuando ejecuto código de usuario arbitrario puedo obtener un StackOverflowException. La pieza que ejecuta el código de usuario está obviamente rodeada por un try - catch, pero los desbordamientos de pila no son detectables bajo circunstancias normales.

He mirado alrededor y this es la respuesta más informativa que pude encontrar, pero aún así me llevó a un callejón sin salida; desde un article in the BCL team's blog encontré que debería usar RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup para llamar al código y al delegado al que se llamaría incluso después de un desbordamiento de pila, pero al intentarlo, el proceso termina con el mensaje de desbordamiento de pila sin que se llame nunca al delegado. Intenté agregar PrePrepareMethodAttribute en el método del controlador, pero eso no cambió nada.

También intenté usar un dominio de aplicación y manejar el evento UnhandledException y el evento DomainUnload, pero todo el proceso se mata en desbordamientos de pila. Lo mismo sucede incluso si yo throw new StackOverflowException(); manualmente y no obtengo un desbordamiento de pila real.

+0

¿Tiene un programa que puede usar scripts para automatizar algún proceso arbitrario, o su programa es un editor de texto para scripts? ¿Es su "código de usuario arbitrario" en sí mismo una secuencia de comandos o algún tipo de complemento IL? –

+0

Ahora mismo mi programa es REPL (loop read-eval-print) para el idioma. – configurator

Respuesta

0

Necesita ejecutar el código en un proceso aparte.

+1

Luego, el usuario seguiría viendo el bloqueo del proceso, incluida la molesta pantalla de Windows que ocurre en esas circunstancias. ¿Cómo detectaría esa situación desde el proceso principal para evitar eso? – configurator

2

Para manejar una excepción que no maneja su código, puede suscribirse a AppDomains UnhandledException - que es lo que maneja el sistema operativo cuando muestra el diálogo que dice que el programa salió inesperadamente.

En el método principal de su programa utilice

var = CurrentDomain AppDomain.CurrentDomain;

y luego agregar un controlador para el evento

currentDomain.UnhandledException + = manejador;

En el controlador, puede hacer lo que desee, como registrar, mostrar un error o incluso reiniciar el programa si lo desea.

+0

Tienes razón, y eso es lo primero que intenté, pero no funcionó. He agregado eso a la pregunta. – configurator

2

Programa el motor de scripts para rastrear el nivel de recursividad en el script. Si la recursión supera un número arbitrariamente grande, entonces mate el script antes de que mate su programa. Alternativamente, podría programar el motor de scripts para que funcione de manera no apilada y almacenar todos los datos de la pila del script en System.Collections.Generic.Stack <T>. Incluso si usa una pila separada, querrá limitar el nivel de recursión que puede tener una secuencia de comandos, pero la colección de la pila le dará unos cientos de veces más espacio en la pila.

+0

Parece que ejecuta IL sin procesar, no su propio intérprete. – SLaks

+0

De hecho, estoy ejecutando mi propio intérprete. Pero me gustaría, si es posible, una solución que también funcione para IL sin procesar. – configurator

+0

Además, un script puede llamar a cualquier función .net, lo que significa que no puedo proteger muy bien la ruta de la llamada. – configurator

0

Debe cargar la secuencia de comandos del usuario, o cualquier complemento externo de terceros, en un dominio de aplicación diferente, para que pueda descargar el dominio de forma segura si se produce un error irrecuperable.

Debe crear un AppDomain diferente ya que no puede descargar un ensamblaje desde un dominio cargado, y no desea cerrar el dominio principal de la aplicación.

se crea un nuevo dominio de aplicación como esta:

var scriptDomain = AppDomain.CreateDomain("User Scripts"); 

, puede cargar cualquier tipo de un montaje que necesita crear. Debe asegurarse de que el objeto que cargue hereda de MarshalByRefObject.

que suponer que la secuencia de comandos de usuario se envuelve dentro de un objeto definido así:

public abstract UserScriptBase : MarshaByRefObject 
{ 
    public abstract void Execute(); 
} 

por lo tanto, que es posible cargar cualquier secuencia de comandos de usuario como esto:

object script = domain.CreateInstanceFromAndUnwrap(type.Location, type.FullName); 

Después de todo eso, puede suscribirse al scriptDomain.UnhandledException y monitoree cualquier error irrecuperable.

El uso de un dominio de aplicación diferente no es fácil y lo más probable es que encuentre algún problema de carga/descarga (los dos dominios hacen referencia a DLL).

Le recomiendo que comparta some tutorial que pueda encontrar en línea.

+2

Esto no va a ayudar. 'StackOverflowException' termina todo el proceso, independientemente de en qué' AppDomain' se haya producido. –

+0

Acepto, que esta es una gran idea, pero desafortunadamente no funciona ... – configurator

+0

@configurator, ¿cómo es una gran idea? si no funciona? Quizás sea inteligente o complicado, pero no genial. – JMCF125