Comment gérer AccessViolationException

185

J'utilise un objet COM (MODI) depuis mon application .net. La méthode que j'appelle lève une exception System.AccessViolationException, qui est interceptée par Visual Studio. La chose étrange est que j'ai encapsulé mon appel dans un try catch, qui a des gestionnaires pour AccessViolationException, COMException et tout le reste, mais lorsque Visual Studio (2010) intercepte AccessViolationException, le débogueur interrompt l'appel de méthode (doc.OCR), et si je passe à travers, il continue à la ligne suivante au lieu d'entrer dans le bloc catch. De plus, si je l'exécute en dehors du studio visuel, mon application se bloque. Comment puis-je gérer cette exception qui est levée dans l'objet 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();

}
Jérémie
la source
Avez-vous essayé de mettre un Exceptiongestionnaire (temporairement!) Pour intercepter toutes les exceptions et voir ce qu'est réellement l'exception ?
ChrisF
3
@ChrisF - oui, voir le dernier gestionnaire de capture? Cela devrait tout attraper, y compris Exception et toute sous-classe d'Exception. De plus, Visual studio rapporte que l'exception est System.AccessViolationException
Jeremy

Réponses:

299

Dans .NET 4.0, le runtime gère certaines exceptions levées en tant qu'erreurs SEH (Windows Structured Error Handling) en tant qu'indicateurs de l'état corrompu. Ces exceptions d'état corrompues (CSE) ne sont pas autorisées à être interceptées par votre code managé standard. Je n'entrerai pas dans le pourquoi ou le comment ici. Lisez cet article sur les CSE dans le Framework .NET 4.0:

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

Mais il y a de l'espoir. Il existe plusieurs façons de contourner ce problème:

  1. Recompilez en tant qu'assembly .NET 3.5 et exécutez-le dans .NET 4.0.

  2. Ajoutez une ligne au fichier de configuration de votre application sous l'élément configuration / runtime: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Décorez les méthodes dans lesquelles vous souhaitez intercepter ces exceptions avec l' HandleProcessCorruptedStateExceptionsattribut. Voir http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 pour plus de détails.


ÉDITER

Auparavant, j'ai référencé un message de forum pour plus de détails. Mais depuis que Microsoft Connect a été retiré, voici les détails supplémentaires au cas où vous seriez intéressé:

De Gaurav Khanna, un développeur de l'équipe Microsoft CLR

Ce comportement est inhérent à la conception en raison d'une fonctionnalité de CLR 4.0 appelée Exceptions d'état corrompu. En termes simples, le code géré ne devrait pas tenter de détecter les exceptions indiquant un état de processus corrompu et AV en fait partie.

Il continue ensuite à référencer la documentation sur le HandleProcessCorruptedStateExceptionsAttribute et l'article ci-dessus. Inutile de dire que cela vaut vraiment la peine d'être lu si vous envisagez d'attraper ces types d'exceptions.

villecoder
la source
12
HandleProcessCorruptedStateExceptionsfonctionne pour moi dans .Net 4.5.
deerchao
2
Merci villecoder, vous êtes un bijou! Je m'occupe de ce problème depuis des semaines, essayant de résoudre le problème à la racine, et je me suis finalement résigné à traiter le symptôme. Votre solution est parfaite.
gadildafissh
19
! Pour être conscient: il est fortement recommandé de terminer le processus après AccessViolationException qui est une exception d'état corrompu (CSE). Sinon, cela peut entraîner des erreurs plus critiques.
Chris W
6
Merci, c'est vraiment utile, même si au début j'ai eu l'impression que je devais faire les 3 étapes pour être en mesure d'attraper ces exceptions, alors que c'est en fait une "logique OR" des façons de le faire. :)
Lou
@deerchao J'espère que vous avez lu le 1er lien fourni en réponse. La gestion des exceptions CSE est une mauvaise idée.
pixel
17

Ajoutez ce qui suit dans le fichier de configuration et il sera intercepté dans le bloc try catch. Un mot d'avertissement ... essayez d'éviter cette situation, car cela signifie qu'une sorte de violation se produit.

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>
Partha
la source
2
Pour ceux qui utilisent c ++ / cli comme dll, le code doit être ajouté au projet .exe supérieur.
Felix
9

Compilé à partir des réponses ci-dessus, a fonctionné pour moi, a fait les étapes suivantes pour l'attraper.

Étape # 1 - Ajouter l'extrait suivant au fichier de configuration

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

Étape 2

Ajouter -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

en haut de la fonction que vous associez attrapez l'exception

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

Mal à l'intérieur
la source
Selon msdn.microsoft.com/en-us/library/… , SecurityCriticalAttribute équivaut à une demande de lien pour une confiance totale. Je ne pense pas que le problème décrit nécessite d'exiger une confiance totale.
Jeremy
0

Microsoft: "Les exceptions d'état de processus corrompu sont des exceptions qui indiquent que l'état d'un processus a été corrompu. Nous vous déconseillons d' exécuter votre application dans cet état ..... Si vous êtes absolument sûr de vouloir conserver la gestion de ces exceptions, vous devez appliquer l' HandleProcessCorruptedStateExceptionsAttributeattribut "

Microsoft: «Utilisez des domaines d'application pour isoler les tâches susceptibles de provoquer l'arrêt d'un processus».

Le programme ci-dessous protégera votre application / thread principale des pannes irrécupérables sans risques associés à l'utilisation de HandleProcessCorruptedStateExceptionset<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
la source