AccessViolationException을 처리하는 방법


185

내 .net 응용 프로그램에서 COM 개체 (MODI)를 사용하고 있습니다. 내가 호출하는 메서드는 Visual Studio에서 가로채는 System.AccessViolationException을 발생시킵니다. 이상한 점은 AccessViolationException, COMException 및 기타 모든 것에 대한 핸들러가있는 try catch로 호출을 래핑했지만 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();

}

Exception모든 예외를 포착하고 실제로 예외 무엇인지 확인하기 위해 핸들러를 (임시로!) 넣었 습니까?
ChrisF

3
@ChrisF-예, 마지막 캐치 핸들러를 보시겠습니까? 그것은 예외와 예외의 하위 클래스를 포함하여 모든 것을 포착해야합니다. 또한 Visual Studio에서는 예외가 System.AccessViolationException
Jeremy

답변:


299

.NET 4.0에서 런타임은 Windows SEH (Structured Error Handling) 오류로 발생한 특정 예외를 손상된 상태 표시기로 처리합니다. 이러한 손상된 상태 예외 (CSE)는 표준 관리 코드에서 잡을 수 없습니다. 나는 왜 또는 어떻게 여기에 들어 가지 않을 것입니다. .NET 4.0 프레임 워크에서 CSE에 대한이 기사를 읽으십시오.

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

그러나 희망이 있습니다. 이 문제를 해결하는 몇 가지 방법이 있습니다.

  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#id0070035 를 참조하십시오.


편집하다

이전에는 자세한 내용 은 포럼 게시물 을 참조했습니다 . 그러나 Microsoft Connect가 폐기되었으므로 다음과 같은 경우 추가 세부 정보가 있습니다.

Microsoft CLR 팀의 개발자 Gaurav Khanna

이 동작은 의도적으로 손상된 상태 예외라는 CLR 4.0의 기능으로 인해 발생합니다. 간단히 말해서 관리 코드는 손상된 프로세스 상태를 나타내는 예외를 포착하려고 시도해서는 안되며 AV는 그 중 하나입니다.

그런 다음 HandleProcessCorruptedStateExceptionsAttribute 및 위 기사 에 대한 문서를 계속 참조합니다 . 말할 것도없이, 이러한 유형의 예외를 잡는 것을 고려하고 있다면 확실히 읽을만한 가치가 있습니다.


12
HandleProcessCorruptedStateExceptions.Net 4.5에서 나를 위해 일합니다.
deerchao

2
고마워 빌 코더, 당신은 보석입니다! 나는 몇 주 동안이 문제를 해결하고 근본 문제를 해결하려고 노력했으며 마침내 증상 치료에 사임했습니다. 당신의 솔루션은 완벽합니다.
gadildafissh

19
! 주의 사항 : 손상된 상태 예외 (CSE) 인 AccessViolationException 후에 프로세스를 종료하는 것이 좋습니다. 그렇지 않으면보다 심각한 오류가 발생할 수 있습니다.
Chris W

6
고마워, 이것은 실제로 도움이되지만 처음에는 이 예외를 잡을 수 있도록 3 단계모두 수행해야한다는 인상을 받았지만 실제로 OR는 그것을 수행하는 방법 의 "논리적 "입니다. :)
Lou

@deerchao 답변에 제공된 첫 번째 링크를 읽었기를 바랍니다. CSE 예외 처리는 좋지 않습니다.
pixel

17

구성 파일에 다음을 추가하면 try catch 블록에 잡 힙니다. 주의 사항 ... 위반이 발생했음을 의미하므로이 상황을 피하십시오.

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

2
c ++ / cli를 dll로 사용하는 사람들은 코드를 최상위 .exe 프로젝트에 추가해야합니다.
Felix

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 Jeremy

0

마이크로 소프트 : "손상된 프로세스 상태의 예외는 프로세스의 상태가 손상되었음을 나타내는 예외입니다. 우리는하지 않는 것이 좋습니다 당신이 있다면 .....이 상태에서 응용 프로그램을 실행 확신 당신이 당신의 취급을 유지하려는 예외는 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
    }
}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.