Jak obsługiwać AccessViolationException

189

Używam obiektu COM (MODI) z mojej aplikacji .net. Wywoływana przeze mnie metoda zgłasza wyjątek System.AccessViolationException, który jest przechwytywany przez program Visual Studio. Dziwne jest to, że zawinąłem moje wywołanie w try catch, który ma programy obsługi dla AccessViolationException, COMException i wszystkiego innego, ale kiedy Visual Studio (2010) przechwytuje AccessViolationException, debugger przerywa wywołanie metody (doc.OCR), a jeśli przejdę przez nie, przechodzi do następnej linii zamiast wchodzić do bloku catch. Dodatkowo, jeśli uruchomię to poza Visual Studio, moja aplikacja ulegnie awarii. Jak mogę obsłużyć ten wyjątek, który jest zgłaszany w obiekcie 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();

}
Jeremy
źródło
Czy próbowałeś umieścić program Exceptionobsługi (tymczasowo!), Aby przechwycić wszystkie wyjątki i zobaczyć, czym właściwie jest wyjątek ?
ChrisF
3
@ChrisF - tak, czy widzisz ostatni program obsługi połowów? Powinno to obejmować wszystko, w tym wyjątek i każdą podklasę wyjątku. Ponadto Visual studio zgłasza, że ​​wyjątkiem jest System.AccessViolationException
Jeremy

Odpowiedzi:

303

W .NET 4.0 środowisko uruchomieniowe obsługuje pewne wyjątki zgłoszone jako błędy obsługi błędów strukturalnych systemu Windows (SEH) jako wskaźniki stanu uszkodzonego. Te wyjątki stanu uszkodzonego (CSE) nie mogą zostać przechwycone przez standardowy kod zarządzany. Nie będę się zagłębiać w dlaczego i jak tu jest. Przeczytaj ten artykuł o CSE w .NET Framework 4.0:

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

Ale jest nadzieja. Można to obejść na kilka sposobów:

  1. Przekompiluj jako zestaw .NET 3.5 i uruchom go w .NET 4.0.

  2. Dodaj wiersz do pliku konfiguracyjnego aplikacji w elemencie configuration / runtime: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Udekoruj metody, w których chcesz przechwycić te wyjątki, HandleProcessCorruptedStateExceptionsatrybutem. Szczegółowe informacje można znaleźć pod adresem http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 .


EDYTOWAĆ

Wcześniej odnosiłem się do posta na forum, aby uzyskać dodatkowe informacje. Ale ponieważ Microsoft Connect został wycofany, oto dodatkowe szczegóły na wypadek zainteresowania:

Od Gaurava Khanny, programisty z zespołu Microsoft CLR

To zachowanie jest zgodne z projektem z powodu funkcji środowiska CLR 4.0 o nazwie Wyjątki uszkodzonego stanu. Mówiąc najprościej, kod zarządzany nie powinien podejmować próby przechwytywania wyjątków, które wskazują na uszkodzony stan procesu, a AV jest jednym z nich.

Następnie przechodzi do dokumentacji dotyczącej obiektu HandleProcessCorruptedStateExceptionsAttribute i do powyższego artykułu. Wystarczy powiedzieć, że zdecydowanie warto przeczytać, jeśli rozważasz wyłapywanie tego typu wyjątków.

villecoder
źródło
12
HandleProcessCorruptedStateExceptionsdziała dla mnie w .Net 4.5.
deerchao
2
Dzięki Villecoder, jesteś klejnotem! Zajmuję się tym problemem od tygodni, próbując rozwiązać problem źródłowy i ostatecznie pogodziłem się z leczeniem objawu. Twoje rozwiązanie jest idealne.
gadildafissh
19
! Uwaga: zdecydowanie zaleca się zakończenie procesu po wyjątku AccessViolationException, który jest wyjątkiem typu Corrupted State Exception (CSE). W przeciwnym razie może to prowadzić do bardziej krytycznych błędów.
Chris W
6
Dzięki, to jest naprawdę pomocne, chociaż na początku odniosłem wrażenie, że muszę zrobić wszystkie 3 kroki, aby móc wyłapać te wyjątki, podczas gdy w rzeczywistości jest to „logiczny OR” sposób na zrobienie tego. :)
Lou
@deerchao Mam nadzieję, że przeczytałeś pierwszy link podany w odpowiedzi. Obsługa wyjątków CSE to zły pomysł.
piksel
17

Dodaj następujący element do pliku konfiguracyjnego, a zostanie on przechwycony w bloku try catch. Słowo ostrzeżenia ... spróbuj uniknąć tej sytuacji, ponieważ oznacza to, że doszło do jakiegoś naruszenia.

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>
Partha
źródło
2
Dla tych, którzy używają C ++ / cli jako biblioteki DLL, kod powinien zostać dodany do głównego projektu .exe.
Felix
9

Zebrane z powyższych odpowiedzi, działały dla mnie, wykonałem następujące kroki, aby to złapać.

Krok # 1 - Dodaj następujący fragment do pliku konfiguracyjnego

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

Krok 2

Dodaj -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

w górnej części funkcji, którą łączysz, złap wyjątek

źródło: http://www.gisremotesensing.com/2017/03/catch-exception-attempted-to-read-or.html

Zły w środku
źródło
Według msdn.microsoft.com/en-us/library/… atrybut SecurityCriticalAttribute jest równoważny żądaniu łącza do pełnego zaufania. Nie sądzę, aby opisany problem wymagał pełnego zaufania.
Jeremy
0

Microsoft: „Wyjątki dotyczące uszkodzonego stanu procesu to wyjątki wskazujące, że stan procesu został uszkodzony. Nie zalecamy uruchamiania aplikacji w tym stanie ..... Jeśli masz absolutną pewność , że chcesz zachować ich obsługę wyjątki, musisz zastosować HandleProcessCorruptedStateExceptionsAttributeatrybut „

Microsoft: „Użyj domen aplikacji do izolowania zadań, które mogą spowolnić proces”.

Poniższy program ochroni twoją główną aplikację / wątek przed nieodwracalnymi awariami bez ryzyka związanego z używaniem HandleProcessCorruptedStateExceptionsi<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
źródło