Як поводитися з AccessViolationException


185

Я використовую об'єкт COM (MODI) з моєї програми .net. Метод, який я викликаю, кидає System.AccessViolationException, який перехоплює Visual Studio. Дивна річ у тому, що я завернув свій дзвінок у пробний ловець, який має обробники для AccessViolationException, COMException та все інше, але коли Visual Studio (2010) перехоплює AccessViolationException, налагоджувач перерветься на виклик методу (doc.OCR), і якщо я переходжу через нього, він продовжує переходити до наступного рядка замість того, щоб вводити блок лову. Крім того, якщо я запускаю це за межами візуальної студії, моя програма виходить з ладу. Як я можу поводитися з цим винятком, який кидається в об'єкт 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 - так, бачите останнього обробника лову? Це має охоплювати все, включаючи виняток та будь-який підклас винятку. Також Visual studio повідомляє, що виняток - System.AccessViolationException
Джеремі

Відповіді:


299

У .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 .


EDIT

Раніше я посилався на повідомлення на форумі, щоб отримати додаткові деталі. Але оскільки Microsoft Connect вийшла на пенсію, ось додаткові деталі на випадок, коли вас цікавить:

Від Гаурава Ханна, розробника з Команди Microsoft CLR

Така поведінка є конструктивною завдяки функції CLR 4.0 під назвою винятки зіпсованих станів. Простіше кажучи, керований код не повинен робити спробу вилучення винятків, які вказують на пошкоджений стан процесу, а AV - один з них.

Потім він переходить до посилання на документацію на HandleProcessCorruptedStateExceptionsAttribute та вищевказану статтю. Досить сказати, що це, безумовно, варто прочитати, якщо ви плануєте виловлювати такі винятки.


12
HandleProcessCorruptedStateExceptionsпрацює для мене в .Net 4.5.
deerchao

2
Спасибі villecoder, ти дорогоцінний камінь! Я займався цим питанням протягом тижнів, намагаючись вирішити корінну проблему, і, нарешті, відмовився від лікування симптому. Ваше рішення ідеальне.
gadildafissh

19
! Будьте в курсі: настійно рекомендується закінчити процес після AccessViolationException, що є винятком пошкодженого стану (CSE). Інакше це може призвести до більш критичних помилок.
Кріс Ш

6
Дякую, це дійсно корисно, хоча спочатку у мене склалося враження, що мені потрібно зробити всі 3 кроки, щоб мати змогу вловити ці винятки, хоча це насправді "логічний OR" спосіб зробити це. :)
Лу

@deerchao Я сподіваюся, що ви прочитали перше посилання, надане у відповіді. Поводження з винятками CSE - погана ідея.
піксель

17

Додайте наступне у конфігураційний файл, і він буде потрапляти у блок спробувати catch. Слово обережності ... намагайтеся уникати такої ситуації, оскільки це означає, що відбувається якесь порушення.

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

2
Для тих, хто використовує c ++ / cli як dll, код слід додати до верхнього проекту .exe.
Фелікс

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

-1

Ви можете спробувати скористатися AppDomain.UnhandledException і побачити, чи дозволяє вам це зловити.

** редагувати *

Ось додаткова інформація, яка може бути корисною (вона читається давно).


2
Ця відповідь більше не є точною через зміни в .NET-рамках. До 4.0 це правильно. За розділом AccessViolationException та спробуйте / ловити блоки в msdn.microsoft.com/en-us/library/…
Тедфорд,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.