Cara menangani AccessViolationException

185

Saya menggunakan objek COM (MODI) dari dalam aplikasi .net saya. Metode yang saya panggil melempar System.AccessViolationException, yang dicegat oleh Visual Studio. Yang aneh adalah bahwa saya telah membungkus panggilan saya dalam try catch, yang memiliki penangan untuk AccessViolationException, COMException dan yang lainnya, tetapi ketika Visual Studio (2010) mencegat AccessViolationException, debugger istirahat pada pemanggilan metode (doc.OCR), dan jika saya melangkah, itu berlanjut ke baris berikutnya alih-alih memasuki blok tangkap. Selain itu, jika saya menjalankan ini di luar studio visual aplikasi saya macet. Bagaimana saya bisa menangani pengecualian ini yang dilemparkan ke dalam objek 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
sumber
Sudahkah Anda mencoba memasukkan Exceptionpenangan (sementara!) Untuk menjebak semua pengecualian dan melihat apa sebenarnya pengecualian itu?
ChrisF
3
@ ChrisF - ya, lihat penangan tangkapan terakhir? Itu harus menangkap semuanya, termasuk Pengecualian dan setiap subkelas Pengecualian. Juga, Visual studio melaporkan bahwa pengecualiannya adalah System.AccessViolationException
Jeremy

Jawaban:

299

Dalam .NET 4.0, runtime menangani pengecualian tertentu yang muncul sebagai kesalahan penanganan terstruktur Windows (SEH) sebagai indikator negara terkorupsi. Pengecualian negara terkorupsi (CSE) ini tidak diizinkan ditangkap oleh kode terkelola standar Anda. Saya tidak akan masuk ke mengapa atau bagaimana di sini. Baca artikel ini tentang CSE di .NET 4.0 Framework:

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

Tapi ada harapan. Ada beberapa cara untuk mengatasi ini:

  1. Kompilasi ulang sebagai perakitan .NET 3.5 dan jalankan dalam .NET 4.0.

  2. Tambahkan baris ke file konfigurasi aplikasi Anda di bawah elemen konfigurasi / runtime: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. Hiasi metode yang Anda ingin tangkap pengecualian ini dengan HandleProcessCorruptedStateExceptionsatribut. Lihat http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 untuk detailnya.


EDIT

Sebelumnya, saya merujuk posting forum untuk rincian tambahan. Tetapi karena Microsoft Connect telah pensiun, berikut adalah detail tambahan jika Anda tertarik:

Dari Gaurav Khanna, seorang pengembang dari Tim CLR Microsoft

Perilaku ini dirancang oleh karena fitur CLR 4.0 yang disebut Pengecualian Status Korup. Sederhananya, kode terkelola seharusnya tidak melakukan upaya untuk menangkap pengecualian yang menunjukkan kondisi proses yang rusak dan AV adalah salah satunya.

Dia kemudian melanjutkan untuk referensi dokumentasi pada HandleProcessCorruptedStateExceptionsAttribute dan artikel di atas. Cukuplah untuk mengatakan, itu layak dibaca jika Anda mempertimbangkan menangkap jenis pengecualian ini.

villecoder
sumber
12
HandleProcessCorruptedStateExceptionsbekerja untuk saya di .Net 4.5.
deerchao
2
Terima kasih villecoder, Anda adalah permata! Saya sudah berhadapan dengan masalah ini selama berminggu-minggu, mencoba menyelesaikan masalah root, dan akhirnya pasrah untuk mengobati gejalanya. Solusi Anda sempurna.
gadildafissh
19
! Untuk diketahui: sangat disarankan untuk mengakhiri proses setelah AccessViolationException yang merupakan Pengecualian Status Korup (CSE). Kalau tidak, itu bisa menyebabkan kesalahan yang lebih kritis.
Chris W
6
Terima kasih, ini sangat membantu, meskipun pada awalnya saya mendapat kesan bahwa saya perlu melakukan semua 3 langkah untuk dapat menangkap pengecualian ini, sementara itu sebenarnya "logis OR" dari cara untuk melakukannya. :)
Lou
@deerchao Saya harap Anda telah membaca tautan 1 yang disediakan sebagai jawaban. Menangani pengecualian CSE adalah ide yang buruk.
piksel
17

Tambahkan berikut ini di file konfigurasi, dan itu akan ditangkap di blok coba tangkap. Kata hati-hati ... cobalah untuk menghindari situasi ini, karena ini berarti beberapa jenis pelanggaran sedang terjadi.

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>
Partha
sumber
2
Bagi mereka yang menggunakan c ++ / cli sebagai dll, kode harus ditambahkan ke proyek .exe atas.
Felix
9

Disusun dari jawaban di atas, bekerja untuk saya, melakukan langkah-langkah berikut untuk menangkapnya.

Langkah # 1 - Tambahkan potongan berikut ke file konfigurasi

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

Langkah 2

Menambahkan -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

di bagian atas fungsi Anda mengikat menangkap pengecualian

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

EvilInside
sumber
Menurut msdn.microsoft.com/en-us/library/… , SecurityCriticalAttribute setara dengan permintaan tautan untuk kepercayaan penuh. Saya tidak berpikir masalah yang dijelaskan mengharuskan menuntut kepercayaan penuh.
Jeremy
0

Microsoft: "Pengecualian keadaan proses terkorupsi adalah pengecualian yang menunjukkan bahwa keadaan suatu proses telah rusak. Kami tidak merekomendasikan untuk menjalankan aplikasi Anda dalam keadaan ini ..... Jika Anda benar - benar yakin bahwa Anda ingin mempertahankan penanganan ini pengecualian, Anda harus menerapkan HandleProcessCorruptedStateExceptionsAttributeatribut "

Microsoft: "Gunakan domain aplikasi untuk mengisolasi tugas yang mungkin menurunkan proses."

Program di bawah ini akan melindungi aplikasi / utas utama Anda dari kegagalan yang tidak dapat dipulihkan tanpa risiko yang terkait dengan penggunaan HandleProcessCorruptedStateExceptionsdan<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
sumber
-1

Anda dapat mencoba menggunakan AppDomain.UnhandledException dan melihat apakah itu memungkinkan Anda menangkapnya.

** EDIT *

Berikut ini beberapa informasi lain yang mungkin berguna (sudah lama dibaca).

Tony Abrams
sumber
2
Jawaban ini tidak lagi lengkap akurat karena perubahan dalam kerangka .NET. Sebelum 4.0 sudah benar. Per bagian AccessViolationException dan coba / tangkap blok di msdn.microsoft.com/en-us/library/…
Tedford