Cómo manejar AccessViolationException

185

Estoy usando un objeto COM (MODI) desde mi aplicación .net. El método al que llamo arroja una System.AccessViolationException, que es interceptada por Visual Studio. Lo extraño es que he envuelto mi llamada en un intento de captura, que tiene controladores para AccessViolationException, COMException y todo lo demás, pero cuando Visual Studio (2010) intercepta AccessViolationException, el depurador interrumpe la llamada al método (doc.OCR), y si paso, continúa a la siguiente línea en lugar de entrar en el bloque de captura. Además, si ejecuto esto fuera del estudio visual, mi aplicación se bloquea. ¿Cómo puedo manejar esta excepción que se lanza dentro del objeto COM?

MODI.Document doc = new MODI.Document();
try
{
    doc.Create(sFileName);
    try
    {
        doc.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, false, false);
        sText = doc.Images[0].Layout.Text;
    }
    catch (System.AccessViolationException ex)
    {
        //MODI seems to get access violations for some reason, but is still able to return the OCR text.
        sText = doc.Images[0].Layout.Text;
    }
    catch (System.Runtime.InteropServices.COMException ex)
    {
        //if no text exists, the engine throws an exception.
        sText = "";
    }
    catch
    {
        sText = "";
    }

    if (sText != null)
    {
        sText = sText.Trim();
    }
}
finally
{
    doc.Close(false);

    //Cleanup routine, this is how we are able to delete files used by MODI.
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc);
    doc = null;
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

}
Jeremy
fuente
¿Has intentado poner un Exceptioncontrolador (temporalmente) para atrapar todas las excepciones y ver cuál es realmente la excepción ?
ChrisF
3
@ChrisF: sí, ¿ves el último controlador de capturas? Eso debería atrapar todo, incluida la Excepción y cualquier subclase de Excepción. Además, Visual Studio informa que la excepción es System.AccessViolationException
Jeremy

Respuestas:

299

En .NET 4.0, el tiempo de ejecución maneja ciertas excepciones generadas como errores de Manejo de errores estructurados de Windows (SEH) como indicadores de estado corrupto. El Código administrado estándar no puede capturar estas Excepciones de estado corrupto (CSE). No voy a entrar en el por qué o cómo está aquí. Lea este artículo sobre CSE en .NET 4.0 Framework:

http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035

Pero hay esperanza. Hay algunas maneras de evitar esto:

  1. Vuelva a compilar como un ensamblado .NET 3.5 y ejecútelo en .NET 4.0.

  2. Agregue una línea al archivo de configuración de su aplicación bajo el elemento de configuración / tiempo de ejecución: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Decora los métodos con los que deseas capturar estas excepciones HandleProcessCorruptedStateExceptions. Consulte http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 para más detalles.


EDITAR

Anteriormente, hice referencia a una publicación del foro para obtener detalles adicionales. Pero dado que Microsoft Connect se retiró, estos son los detalles adicionales en caso de que le interese:

De Gaurav Khanna, desarrollador del equipo Microsoft CLR

Este comportamiento es por diseño debido a una característica de CLR 4.0 llamada Excepciones de estado corrupto. En pocas palabras, el código administrado no debería intentar detectar excepciones que indican un estado de proceso dañado y AV es una de ellas.

Luego continúa haciendo referencia a la documentación en HandleProcessCorruptedStateExceptionsAttribute y el artículo anterior. Baste decir que definitivamente vale la pena leerlo si estás considerando atrapar este tipo de excepciones.

villecoder
fuente
12
HandleProcessCorruptedStateExceptionsfunciona para mí en .Net 4.5.
ciervo
2
Gracias villecoder, eres una joya! He estado lidiando con este problema durante semanas, tratando de resolver el problema raíz, y finalmente me resigné a tratar el síntoma. Tu solución es perfecta.
gadildafissh
19
! Para tener en cuenta: se recomienda finalizar el proceso después de AccessViolationException que es una excepción de estado corrupta (CSE). De lo contrario, puede conducir a errores más críticos.
Chris W
66
Gracias, esto es realmente útil, aunque al principio tuve la impresión de que tengo que hacer los 3 pasos para poder detectar estas excepciones, mientras que en realidad es una "lógica OR" de las formas de hacerlo. :)
Lou
@deerchao Espero que hayas leído el primer enlace proporcionado en respuesta. Manejar excepciones CSE es una mala idea.
píxel
17

Agregue lo siguiente en el archivo de configuración, y se detectará en el bloque try catch. Palabra de precaución ... trate de evitar esta situación, ya que esto significa que está ocurriendo algún tipo de violación.

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>
Partha
fuente
2
Para aquellos que usan c ++ / cli como dll, el código debe agregarse al proyecto superior .exe.
Felix
9

Compilado a partir de las respuestas anteriores, funcionó para mí, siguió los pasos para atraparlo.

Paso # 1 - Agregue el siguiente fragmento al archivo de configuración

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>

Paso 2

Agregar -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

en la parte superior de la función que está atando, capture la excepción

fuente: http://www.gisremotesensing.com/2017/03/catch-exception-attempted-to-read-or.html

Maldad interior
fuente
De acuerdo con msdn.microsoft.com/en-us/library/… , SecurityCriticalAttribute es equivalente a una demanda de enlace para una confianza total. No creo que el problema descrito requiera exigir plena confianza.
Jeremy
0

Microsoft: "Las excepciones de estado de proceso dañado son excepciones que indican que el estado de un proceso ha sido dañado. No recomendamos ejecutar su aplicación en este estado ..... Si está absolutamente seguro de que desea mantener su manejo de estos excepciones, debe aplicar el HandleProcessCorruptedStateExceptionsAttributeatributo "

Microsoft: "Use dominios de aplicación para aislar las tareas que pueden provocar un proceso".

El siguiente programa protegerá su aplicación / hilo principal de fallas irrecuperables sin riesgos asociados con el uso HandleProcessCorruptedStateExceptionsy<legacyCorruptedStateExceptionsPolicy>

public class BoundaryLessExecHelper : MarshalByRefObject
{
    public void DoSomething(MethodParams parms, Action action)
    {
        if (action != null)
            action();
        parms.BeenThere = true; // example of return value
    }
}

public struct MethodParams
{
    public bool BeenThere { get; set; }
}

class Program
{
    static void InvokeCse()
    {
        IntPtr ptr = new IntPtr(123);
        System.Runtime.InteropServices.Marshal.StructureToPtr(123, ptr, true);
    }

    private static void ExecInThisDomain()
    {
        try
        {
            var o = new BoundaryLessExecHelper();
            var p = new MethodParams() { BeenThere = false };
            Console.WriteLine("Before call");

            o.DoSomething(p, CausesAccessViolation);
            Console.WriteLine("After call. param been there? : " + p.BeenThere.ToString()); //never stops here
        }
        catch (Exception exc)
        {
            Console.WriteLine($"CSE: {exc.ToString()}");
        }
        Console.ReadLine();
    }


    private static void ExecInAnotherDomain()
    {
        AppDomain dom = null;

        try
        {
            dom = AppDomain.CreateDomain("newDomain");
            var p = new MethodParams() { BeenThere = false };
            var o = (BoundaryLessExecHelper)dom.CreateInstanceAndUnwrap(typeof(BoundaryLessExecHelper).Assembly.FullName, typeof(BoundaryLessExecHelper).FullName);         
            Console.WriteLine("Before call");

            o.DoSomething(p, CausesAccessViolation);
            Console.WriteLine("After call. param been there? : " + p.BeenThere.ToString()); // never gets to here
        }
        catch (Exception exc)
        {
            Console.WriteLine($"CSE: {exc.ToString()}");
        }
        finally
        {
            AppDomain.Unload(dom);
        }

        Console.ReadLine();
    }


    static void Main(string[] args)
    {
        ExecInAnotherDomain(); // this will not break app
        ExecInThisDomain();  // this will
    }
}
TS
fuente