Как обрабатывать AccessViolationException

189

Я использую COM-объект (MODI) из моего приложения .net. Метод, который я вызываю, вызывает исключение System.AccessViolationException, которое перехватывается Visual Studio. Странно то, что я заключил свой вызов в try catch, у которого есть обработчики для AccessViolationException, COMException и всего остального, но когда Visual Studio (2010) перехватывает AccessViolationException, отладчик прерывает вызов метода (doc.OCR), и если я перейду, он перейдет к следующей строке вместо входа в блок catch. Кроме того, если я запускаю это за пределами визуальной студии, мое приложение вылетает. Как я могу обработать это исключение, которое возникает в 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();

}
Джереми
источник
Вы пробовали поместить Exceptionобработчик (временно!), Чтобы перехватить все исключения и посмотреть, что это за исключение на самом деле ?
ChrisF
3
@ChrisF - да, видел последний обработчик уловов? Это должно уловить все, включая Exception и любой подкласс Exception. Кроме того, Visual studio сообщает, что исключением является System.AccessViolationException
Джереми,

Ответы:

303

В .NET 4.0 среда выполнения обрабатывает определенные исключения, возникающие как ошибки структурной обработки ошибок Windows (SEH), как индикаторы поврежденного состояния. Эти исключения поврежденного состояния (CSE) не могут быть обнаружены вашим стандартным управляемым кодом. Я не буду вдаваться в подробности почему и как здесь. Прочтите эту статью о CSE в .NET 4.0 Framework:

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

Но есть надежда. Есть несколько способов обойти это:

  1. Перекомпилируйте как сборку .NET 3.5 и запустите ее в .NET 4.0.

  2. Добавьте строку в файл конфигурации вашего приложения под элементом конфигурации / времени выполнения: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Украсьте методы, в которых вы хотите перехватывать эти исключения, HandleProcessCorruptedStateExceptionsатрибутом. Подробности см. На http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 .


РЕДАКТИРОВАТЬ

Ранее я ссылался на сообщение на форуме для получения дополнительных сведений. Но поскольку Microsoft Connect больше не работает, вот дополнительные сведения, если вам это интересно:

От Гаурава Ханна, разработчика из команды Microsoft CLR.

Такое поведение является конструктивным из-за функции CLR 4.0, называемой Исключениями поврежденного состояния. Проще говоря, управляемый код не должен пытаться перехватывать исключения, которые указывают на поврежденное состояние процесса, и AV является одним из них.

Затем он обращается к документации по атрибуту HandleProcessCorruptedStateExceptionsAttribute и указанной выше статье. Достаточно сказать, что это определенно стоит прочитать, если вы планируете перехватить эти типы исключений.

вилкодер
источник
12
HandleProcessCorruptedStateExceptionsу меня работает в .Net 4.5.
deerchao
2
Спасибо villecoder, вы жемчужина! Я занимался этой проблемой в течение нескольких недель, пытаясь решить основную проблему и, наконец, смирился с лечением симптома. Ваше решение идеальное.
gadildafissh
19
! На заметку: настоятельно рекомендуется завершить процесс после исключения AccessViolationException, которое представляет собой исключение поврежденного состояния (CSE). В противном случае это может привести к более серьезным ошибкам.
Chris W
6
Спасибо, это действительно полезно, хотя сначала у меня создалось впечатление, что мне нужно выполнить все 3 шага , чтобы уловить эти исключения, хотя на самом деле это «логичный OR» способ сделать это. :)
Лу
@deerchao Надеюсь, вы прочитали первую ссылку в ответе. Обработка исключений CSE - плохая идея.
pixel
17

Добавьте следующее в конфигурационный файл, и он будет пойман в блоке try catch. Предупреждение ... старайтесь избегать этой ситуации, так как это означает, что происходит какое-то нарушение.

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>
Партха
источник
2
Для тех, кто использует c ++ / cli в качестве dll, код должен быть добавлен в топ .exe проекта.
Felix
9

Скомпилированный из приведенных выше ответов, работал у меня, сделал следующие шаги, чтобы его поймать.

Шаг №1 - Добавьте следующий фрагмент в файл конфигурации

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

Шаг 2

Добавить -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

в верхней части функции, которую вы связываете, поймайте исключение

источник: http://www.gisremotesensing.com/2017/03/catch-exception-attempted-to-read-or.html

Зло внутри
источник
Согласно msdn.microsoft.com/en-us/library/… , атрибут SecurityCriticalAttribute эквивалентен запросу ссылки для полного доверия. Не думаю, что описанная проблема требует полного доверия.
Джереми
0

Microsoft: «Исключения из состояния поврежденного процесса - это исключения, которые указывают на то, что состояние процесса было повреждено. Мы не рекомендуем запускать ваше приложение в этом состоянии ..... Если вы абсолютно уверены, что хотите продолжить обработку этих исключения, вы должны применить HandleProcessCorruptedStateExceptionsAttributeатрибут "

Microsoft: «Используйте домены приложений, чтобы изолировать задачи, которые могут остановить процесс».

Приведенная ниже программа защитит ваше основное приложение / поток от неисправимых сбоев без рисков, связанных с использованием HandleProcessCorruptedStateExceptionsи<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
источник
-1

Вы можете попробовать использовать AppDomain.UnhandledException и посмотреть, позволит ли вам его поймать.

**РЕДАКТИРОВАТЬ*

Вот еще некоторая информация, которая может быть полезна (это долгое чтение).

Тони Абрамс
источник
2
Этот ответ больше не является точным из-за изменений в платформе .NET. До 4.0 это правильно. Для раздела AccessViolationException и блоков try / catch в msdn.microsoft.com/en-us/library/…
Тедфорд,