Как обращаться с AccessViolationException

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

}

4 ответа

Решение

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

Шаг 2

Добавлять -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

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

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

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
    }
}

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

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

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

Другие вопросы по тегам