วิธีจัดการ AccessViolationException


185

ฉันกำลังใช้วัตถุ COM (MODI) จากภายในแอปพลิเคชัน. net ของฉัน วิธีการที่ฉันเรียกใช้จะเป็นการ System.AccessViolationException ซึ่งถูกสกัดกั้นโดย Visual Studio สิ่งที่แปลกก็คือฉันมีการโทรของฉันในลอง catch ซึ่งมีตัวจัดการสำหรับ AccessViolationException, COMException และทุกอย่างอื่น แต่เมื่อ Visual Studio (2010) ดัก AccessViolationException ดีบักเกอร์จะแบ่งการเรียกเมธอด (doc.OCR) และถ้าฉันผ่านมันไปยังบรรทัดถัดไปแทนที่จะเข้าสู่ catch 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 - ใช่เห็นตัวจัดการล่าสุด? ที่ควรจับทุกอย่างรวมถึงข้อยกเว้นและคลาสย่อยของข้อยกเว้น เช่นกัน Visual Studio รายงานว่าข้อยกเว้นคือ System.AccessViolationException
Jeremy

คำตอบ:


299

ใน. NET 4.0 รันไทม์จัดการข้อยกเว้นบางอย่างที่เกิดขึ้นเป็นข้อผิดพลาด Windows Structured Error Handling (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 เลิกใช้แล้วนี่เป็นรายละเอียดเพิ่มเติมในกรณีที่คุณสนใจ:

จาก Gaurav Khanna ผู้พัฒนาจาก Microsoft CLR Team

พฤติกรรมนี้เกิดจากการออกแบบเนื่องจากคุณสมบัติของ CLR 4.0 เรียกว่าข้อยกเว้นสถานะเสียหาย ใส่รหัสที่มีการจัดการไม่ควรพยายามตรวจจับข้อยกเว้นที่ระบุสถานะกระบวนการที่เสียหายและ AV เป็นหนึ่งในนั้น

จากนั้นเขาก็ไปอ้างอิงเอกสารในHandleProcessCorruptedStateExceptionsAttributeและบทความข้างต้น พอจะบอกได้ว่ามันคุ้มค่าที่จะอ่านหากคุณกำลังพิจารณาข้อยกเว้นประเภทนี้


12
HandleProcessCorruptedStateExceptionsเหมาะกับฉันใน. Net 4.5
Deerchao

2
ขอบคุณ villecoder คุณเป็นอัญมณี! ฉันได้รับการจัดการกับปัญหานี้มาหลายสัปดาห์พยายามที่จะแก้ปัญหารากและในที่สุดก็ลาออกจากตัวเองเพื่อรักษาอาการ ทางออกของคุณสมบูรณ์แบบ
gadildafissh

19
! ที่จะต้องทราบ: ขอแนะนำอย่างยิ่งที่จะจบกระบวนการหลังจาก AccessViolationException ที่เป็นข้อยกเว้นของรัฐที่เสียหาย (CSE) มิฉะนั้นอาจนำไปสู่ข้อผิดพลาดที่สำคัญยิ่งขึ้น
Chris W

6
ขอบคุณสิ่งนี้มีประโยชน์จริง ๆ แต่ในตอนแรกฉันรู้สึกว่าฉันต้องทำทั้ง 3 ขั้นตอนเพื่อให้สามารถตรวจจับข้อยกเว้นเหล่านี้ได้ในขณะที่มันเป็น "ตรรกะOR" ของวิธีการทำ :)
Lou

@deerchao ฉันหวังว่าคุณได้อ่านลิงค์แรกที่ให้ไว้ในการตอบ การจัดการข้อยกเว้น CSE เป็นแนวคิดที่ไม่ดี
พิกเซล

17

เพิ่มสิ่งต่อไปนี้ในไฟล์กำหนดค่าและจะถูกจับในลอง catch catch block คำเตือน ... พยายามหลีกเลี่ยงสถานการณ์นี้เพราะนี่หมายถึงการละเมิดบางอย่างกำลังเกิดขึ้น

<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 เทียบเท่ากับความต้องการลิงก์สำหรับความน่าเชื่อถือแบบเต็ม ฉันไม่คิดว่าปัญหาที่อธิบายไว้จำเป็นต้องมีความไว้วางใจอย่างเต็มที่
Jeremy

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 Framework ก่อนหน้า 4.0 มันถูกต้อง ต่อส่วนของ AccessViolationException และลอง / จับบล็อกในmsdn.microsoft.com/en-us/library/…
Tedford
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.