AccessViolationExceptionの処理方法


185

.netアプリケーション内からCOMオブジェクト(MODI)を使用しています。私が呼び出しているメソッドはSystem.AccessViolationExceptionをスローしますが、これはVisual Studioによってインターセプトされます。奇妙なことに、AccessViolationException、COMException、その他すべてのハンドラーがあるトライキャッチに呼び出しをラップしましたが、Visual Studio(2010)がAccessViolationExceptionをインターセプトすると、デバッガーはメソッド呼び出し(doc.OCR)で中断します。そして、一歩進んだ場合、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-はい、最後のキャッチハンドラを参照してください。これは、ExceptionとExceptionのサブクラスを含むすべてをキャッチします。同様に、Visual Studioは例外がSystem.AccessViolationException
Jeremy

回答:


299

.NET 4.0では、ランタイムはWindows構造化エラー処理(SEH)エラーとして発生した特定の例外を破損状態のインジケーターとして処理します。これらの破損状態例外(CSE)は、標準のマネージコードでキャッチすることはできません。ここにある理由や方法については説明しません。.NET 4.0 Frameworkの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から

この動作は、Corrupted State Exceptionsと呼ばれるCLR 4.0の機能によるものです。簡単に言えば、マネージコードは破損したプロセス状態を示す例外をキャッチしようとするべきではなく、AVはその1つです。

次に、HandleProcessCorruptedStateExceptionsAttributeに関するドキュメントと上記の記事を引き続き参照します。これらの種類の例外のキャッチを検討している場合は、一読の価値があります。


12
HandleProcessCorruptedStateExceptions.Net 4.5で動作します。
Deerchao 2013年

2
villecoderに感謝します、あなたは宝石です!私はこの問題に何週間も対処し、根本的な問題を解決しようとしましたが、最終的にこの症状の治療に辞任しました。あなたの解決策は完璧です。
gadildafissh 2013

19
!注意:破損状態例外(CSE)であるAccessViolationExceptionの後でプロセスを終了することを強くお勧めします。そうしないと、より重大なエラーが発生する可能性があります。
クリスW

6
ありがとう、これは本当に役に立ちますが、最初はこれらの例外をキャッチするために3つのステップすべてを実行する必要があるという印象を受けましたが、実際ORにはそれを行う方法の「論理的」なものです。:)
Lou

@deerchao私はあなたが答えで提供された最初のリンクを読んだことを望みます。CSE例外の処理は悪い考えです。
ピクセル

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は完全信頼のリンク要求に相当します。記述されている問題が完全な信頼を要求する必要があるとは思いません。
ジェレミー

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

-1

あなたは使用して試すことができAppDomain.UnhandledExceptionをし、それはあなたがそれをキャッチすることができますかどうかを確認します。

**編集*

役に立つかもしれないいくつかのより多くの情報はここにあります(それは長い読みです)。


2
.NETフレームワークの変更により、この回答は完全に正確ではなくなりました。4.0より前は正しいです。msdn.microsoft.com/en-us/library/…
Tedford
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.