Come gestire AccessViolationException


185

Sto usando un oggetto COM (MODI) dalla mia applicazione .net. Il metodo che sto chiamando genera System.AccessViolationException, che viene intercettato da Visual Studio. La cosa strana è che ho racchiuso la mia chiamata in un tentativo catch, che ha gestori per AccessViolationException, COMException e tutto il resto, ma quando Visual Studio (2010) intercetta AccessViolationException, il debugger si interrompe sulla chiamata del metodo (doc.OCR), e se passo attraverso, continua alla riga successiva invece di entrare nel blocco catch. Inoltre, se eseguo questo al di fuori di Visual Studio, la mia applicazione si arresta in modo anomalo. Come posso gestire questa eccezione che viene generata all'interno dell'oggetto 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();

}

Hai provato a inserire un Exceptiongestore (temporaneamente!) Per intercettare tutte le eccezioni e vedere qual è effettivamente l'eccezione ?
ChrisF

3
@ChrisF - sì, vedi l'ultimo gestore delle catture? Ciò dovrebbe catturare tutto, compresa l'eccezione e qualsiasi sottoclasse di eccezione. Inoltre, Visual Studio riporta che l'eccezione è System.AccessViolationException
Jeremy

Risposte:


299

In .NET 4.0, il runtime gestisce alcune eccezioni sollevate come errori SEH (Windows Structured Error Handling) come indicatori di stato danneggiato. Queste eccezioni di stato danneggiato (CSE) non possono essere rilevate dal codice gestito standard. Non entrerò nel perché o come è qui. Leggi questo articolo sui CSE in .NET 4.0 Framework:

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

Ma c'è speranza. Ci sono alcuni modi per aggirare questo:

  1. Ricompilare come assembly .NET 3.5 ed eseguirlo in .NET 4.0.

  2. Aggiungi una riga al file di configurazione dell'applicazione sotto l'elemento configuration / runtime: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Decora i metodi in cui vuoi catturare queste eccezioni con l' HandleProcessCorruptedStateExceptionsattributo. Vedere http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 per i dettagli.


MODIFICARE

In precedenza, ho fatto riferimento a un post sul forum per ulteriori dettagli. Ma poiché Microsoft Connect è stato ritirato, ecco i dettagli aggiuntivi nel caso in cui ti interessi:

Da Gaurav Khanna, uno sviluppatore del team Microsoft CLR

Questo comportamento è in base alla progettazione a causa di una funzionalità di CLR 4.0 denominata Eccezioni stato danneggiato. In poche parole, il codice gestito non dovrebbe tentare di rilevare eccezioni che indicano lo stato del processo danneggiato e AV è uno di questi.

Continua quindi a fare riferimento alla documentazione su HandleProcessCorruptedStateExceptionsAttribute e all'articolo precedente. Basti dire che vale sicuramente la pena leggere se stai pensando di cogliere questo tipo di eccezioni.


12
HandleProcessCorruptedStateExceptionsfunziona per me in .Net 4.5.
deerchao,

2
Grazie villecoder, sei un gioiello! Ho affrontato questo problema per settimane, cercando di risolvere il problema alla radice e alla fine mi sono dimesso dal trattamento del sintomo. La tua soluzione è perfetta
gadildafissh,

19
! Da tenere presente: si consiglia vivamente di terminare il processo dopo AccessViolationException che è un'eccezione di stato danneggiato (CSE). Altrimenti può portare a errori più critici.
Chris W,

6
Grazie, questo è davvero utile, anche se all'inizio ho avuto l'impressione di dover fare tutti e 3 i passaggi per essere in grado di cogliere queste eccezioni, mentre in realtà è un "logico OR" dei modi per farlo. :)
Lou,

@deerchao Spero che tu abbia letto il primo link fornito in risposta. Gestire le eccezioni CSE è una cattiva idea.
pixel,

17

Aggiungi quanto segue nel file di configurazione e verrà catturato nel tentativo di catch block. Un avvertimento ... cerca di evitare questa situazione, poiché ciò significa che si sta verificando una sorta di violazione.

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

2
Per coloro che usano c ++ / cli come dll, il codice dovrebbe essere aggiunto al progetto .exe superiore.
Felix,

9

Compilato dalle risposte sopra, ha funzionato per me, ha fatto i seguenti passi per prenderlo.

Passaggio 1: aggiungi il frammento seguente al file di configurazione

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

Passo 2

Inserisci -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

nella parte superiore della funzione che stai catturando cattura l'eccezione

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


Secondo msdn.microsoft.com/en-us/library/… , SecurityCriticalAttribute equivale a una richiesta di collegamento per la piena fiducia. Non credo che il problema descritto richieda la piena fiducia.
Jeremy,

0

Microsoft: "Le eccezioni di stato del processo danneggiate sono eccezioni che indicano che lo stato di un processo è stato danneggiato. Non è consigliabile eseguire l'applicazione in questo stato ..... Se si è assolutamente certi di voler mantenere la gestione di questi eccezioni, è necessario applicare l' HandleProcessCorruptedStateExceptionsAttributeattributo "

Microsoft: "Utilizza i domini delle applicazioni per isolare le attività che potrebbero arrestare un processo."

Il seguente programma proteggerà la tua applicazione / thread principale da guasti irreversibili senza rischi associati all'uso di HandleProcessCorruptedStateExceptionse<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
    }
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.