72

私が書いている内のメソッドStackOverflowExceptionへの呼び出しから取得している を防止または処理したいと思います。問題は、ユーザーが無限に再帰的な を記述できるように思われ、メソッドの呼び出しで爆発するだけです。(つまり、問題は、通常、このような例外の原因である典型的なプログラム エラーだけではありません。)XslCompiledTransform.TransformXsl EditorXsl scriptTransform

許可される再帰の回数を検出および/または制限する方法はありますか? または、このコードが爆発しないようにするための他のアイデアはありますか?

4

10 に答える 10

71

マイクロソフトから:

.NET Framework バージョン 2.0 以降では、StackOverflowException オブジェクトを try-catch ブロックでキャッチできず、対応するプロセスが既定で終了します。したがって、スタック オーバーフローを検出して防止するコードを記述することをお勧めします。たとえば、アプリケーションが再帰に依存している場合は、カウンターまたは状態条件を使用して再帰ループを終了します。

コード内ではなく、内部 .NET メソッド内で例外が発生していると想定しています。

いくつかのことができます。

  • xsl の無限再帰をチェックし、変換を適用する前にユーザーに通知するコードを記述します (うーん)。
  • XslTransform コードを別のプロセスにロードします (ハックですが、作業は少なくなります)。

Process クラスを使用して、変換を別のプロセスに適用するアセンブリをロードし、メイン アプリを強制終了することなく、プロセスが停止した場合にユーザーにエラーを警告できます。

編集:私はちょうどテストしました、これがそれを行う方法です:

メインプロセス:

// This is just an example, obviously you'll want to pass args to this.
Process p1 = new Process();
p1.StartInfo.FileName = "ApplyTransform.exe";
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

p1.Start();
p1.WaitForExit();

if (p1.ExitCode == 1)    
   Console.WriteLine("StackOverflow was thrown");

ApplyTransform プロセス:

class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        throw new StackOverflowException();
    }

    // We trap this, we can't save the process, 
    // but we can prevent the "ILLEGAL OPERATION" window 
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        if (e.IsTerminating)
        {
            Environment.Exit(1);
        }
    }
}
于 2008-10-15T22:56:17.797 に答える
26

@WilliamJockusch による報奨金の質問と元の質問は異なります。

この回答は、サードパーティのライブラリの一般的なケースでの StackOverflow と、それらでできる/できないことについてです。XslTransform の特殊なケースを探している場合は、受け入れられた回答を参照してください。


スタック オーバーフローは、スタック上のデータが特定の制限 (バイト単位) を超えるために発生します。この検出のしくみの詳細については、こちらを参照してください

StackOverflowExceptions を追跡する一般的な方法があるかどうか疑問に思っています。言い換えれば、コードのどこかに無限再帰があるとしますが、どこにあるのかわかりません。それが起こっているのを見るまで、コードをあちこちにステップ実行するよりも簡単な方法で追跡したいと思います。私はそれがどれほどハックであるかは気にしません。

リンクで述べたように、静的コード分析からスタック オーバーフローを検出するには、決定不能な停止問題を解決する必要があります。特効薬がないことを確認したので、問題を突き止めるのに役立つと思われるいくつかのトリックを紹介します。

この質問はさまざまな方法で解釈できると思います。私は少し退屈なので :-)、さまざまなバリエーションに分解します。

テスト環境でのスタック オーバーフローの検出

基本的にここでの問題は、(制限された) テスト環境があり、(拡張された) 実稼働環境でスタック オーバーフローを検出したいということです。

SO 自体を検出する代わりに、スタックの深さを設定できるという事実を利用してこれを解決します。デバッガーは、必要なすべての情報を提供します。ほとんどの言語では、スタック サイズまたは最大再帰深度を指定できます。

基本的に、スタックの深さをできるだけ小さくすることで SO を強制しようとします。オーバーフローしない場合は、本番環境用にいつでも大きく (=この場合はより安全に) することができます。スタック オーバーフローが発生した瞬間に、それが「有効な」ものかどうかを手動で判断できます。

これを行うには、スタック サイズ (この場合は小さい値) を Thread パラメーターに渡し、何が起こるかを確認します。.NET のデフォルトのスタック サイズは 1 MB です。これよりもずっと小さい値を使用します。

class StackOverflowDetector
{
    static int Recur()
    {
        int variable = 1;
        return variable + Recur();
    }

    static void Start()
    {
        int depth = 1 + Recur();
    }

    static void Main(string[] args)
    {
        Thread t = new Thread(Start, 1);
        t.Start();
        t.Join();
        Console.WriteLine();
        Console.ReadLine();
    }
}

注: 以下でもこのコードを使用します。

オーバーフローしたら、意味のある SO が得られるまで、より大きな値に設定できます。

SOの前に例外を作成する

StackOverflowExceptionキャッチできません。これは、それが起こったときにできることはあまりないことを意味します。そのため、コードで何か問題が発生する可能性があると思われる場合は、場合によっては独自の例外を作成できます。これに必要なのは、現在のスタックの深さだけです。カウンターは必要ありません。.NET の実際の値を使用できます。

class StackOverflowDetector
{
    static void CheckStackDepth()
    {
        if (new StackTrace().FrameCount > 10) // some arbitrary limit
        {
            throw new StackOverflowException("Bad thread.");
        }
    }

    static int Recur()
    {
        CheckStackDepth();
        int variable = 1;
        return variable + Recur();
    }

    static void Main(string[] args)
    {
        try
        {
            int depth = 1 + Recur();
        }
        catch (ThreadAbortException e)
        {
            Console.WriteLine("We've been a {0}", e.ExceptionState);
        }
        Console.WriteLine();
        Console.ReadLine();
    }
}

このアプローチは、コールバック メカニズムを使用するサードパーティ コンポーネントを扱っている場合にも機能することに注意してください。必要なのは、スタック トレースでいくつかの呼び出しをインターセプトできることだけです。

別スレッドでの検出

あなたはこれを明示的に提案したので、これに行きます。

別のスレッドで SO の検出を試みることができますが、おそらく何の役にも立たないでしょう。スタック オーバーフローは、コンテキスト スイッチを取得する前であっても、すぐに発生する可能性があります。これは、このメカニズムがまったく信頼できないことを意味します...実際に使用することはお勧めしません。ビルドするのは楽しかったので、ここにコードを示します :-)

class StackOverflowDetector
{
    static int Recur()
    {
        Thread.Sleep(1); // simulate that we're actually doing something :-)
        int variable = 1;
        return variable + Recur();
    }

    static void Start()
    {
        try
        {
            int depth = 1 + Recur();
        }
        catch (ThreadAbortException e)
        {
            Console.WriteLine("We've been a {0}", e.ExceptionState);
        }
    }

    static void Main(string[] args)
    {
        // Prepare the execution thread
        Thread t = new Thread(Start);
        t.Priority = ThreadPriority.Lowest;

        // Create the watch thread
        Thread watcher = new Thread(Watcher);
        watcher.Priority = ThreadPriority.Highest;
        watcher.Start(t);

        // Start the execution thread
        t.Start();
        t.Join();

        watcher.Abort();
        Console.WriteLine();
        Console.ReadLine();
    }

    private static void Watcher(object o)
    {
        Thread towatch = (Thread)o;

        while (true)
        {
            if (towatch.ThreadState == System.Threading.ThreadState.Running)
            {
                towatch.Suspend();
                var frames = new System.Diagnostics.StackTrace(towatch, false);
                if (frames.FrameCount > 20)
                {
                    towatch.Resume();
                    towatch.Abort("Bad bad thread!");
                }
                else
                {
                    towatch.Resume();
                }
            }
        }
    }
}

これをデバッガで実行して、何が起こるか楽しみましょう。

スタック オーバーフローの特性を利用する

あなたの質問の別の解釈は、「スタックオーバーフロー例外を引き起こす可能性のあるコードはどこにありますか?」です。明らかに、これの答えは次のとおりです。再帰を伴うすべてのコード。コードの各部分について、手動で分析を行うことができます。

これは、静的コード分析を使用して判断することもできます。そのために必要なことは、すべてのメソッドを逆コンパイルし、それらに無限再帰が含まれているかどうかを調べることです。これを行うコードを次に示します。

// A simple decompiler that extracts all method tokens (that is: call, callvirt, newobj in IL)
internal class Decompiler
{
    private Decompiler() { }

    static Decompiler()
    {
        singleByteOpcodes = new OpCode[0x100];
        multiByteOpcodes = new OpCode[0x100];
        FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
        for (int num1 = 0; num1 < infoArray1.Length; num1++)
        {
            FieldInfo info1 = infoArray1[num1];
            if (info1.FieldType == typeof(OpCode))
            {
                OpCode code1 = (OpCode)info1.GetValue(null);
                ushort num2 = (ushort)code1.Value;
                if (num2 < 0x100)
                {
                    singleByteOpcodes[(int)num2] = code1;
                }
                else
                {
                    if ((num2 & 0xff00) != 0xfe00)
                    {
                        throw new Exception("Invalid opcode: " + num2.ToString());
                    }
                    multiByteOpcodes[num2 & 0xff] = code1;
                }
            }
        }
    }

    private static OpCode[] singleByteOpcodes;
    private static OpCode[] multiByteOpcodes;

    public static MethodBase[] Decompile(MethodBase mi, byte[] ildata)
    {
        HashSet<MethodBase> result = new HashSet<MethodBase>();

        Module module = mi.Module;

        int position = 0;
        while (position < ildata.Length)
        {
            OpCode code = OpCodes.Nop;

            ushort b = ildata[position++];
            if (b != 0xfe)
            {
                code = singleByteOpcodes[b];
            }
            else
            {
                b = ildata[position++];
                code = multiByteOpcodes[b];
                b |= (ushort)(0xfe00);
            }

            switch (code.OperandType)
            {
                case OperandType.InlineNone:
                    break;
                case OperandType.ShortInlineBrTarget:
                case OperandType.ShortInlineI:
                case OperandType.ShortInlineVar:
                    position += 1;
                    break;
                case OperandType.InlineVar:
                    position += 2;
                    break;
                case OperandType.InlineBrTarget:
                case OperandType.InlineField:
                case OperandType.InlineI:
                case OperandType.InlineSig:
                case OperandType.InlineString:
                case OperandType.InlineTok:
                case OperandType.InlineType:
                case OperandType.ShortInlineR:
                    position += 4;
                    break;
                case OperandType.InlineR:
                case OperandType.InlineI8:
                    position += 8;
                    break;
                case OperandType.InlineSwitch:
                    int count = BitConverter.ToInt32(ildata, position);
                    position += count * 4 + 4;
                    break;

                case OperandType.InlineMethod:
                    int methodId = BitConverter.ToInt32(ildata, position);
                    position += 4;
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            result.Add((MethodBase)module.ResolveMember(methodId, mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes));
                        }
                        else
                        {
                            result.Add((MethodBase)module.ResolveMember(methodId, mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments()));
                        }
                    }
                    catch { } 
                    break;
                

                default:
                    throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType);
            }
        }
        return result.ToArray();
    }
}

class StackOverflowDetector
{
    // This method will be found:
    static int Recur()
    {
        CheckStackDepth();
        int variable = 1;
        return variable + Recur();
    }

    static void Main(string[] args)
    {
        RecursionDetector();
        Console.WriteLine();
        Console.ReadLine();
    }

    static void RecursionDetector()
    {
        // First decompile all methods in the assembly:
        Dictionary<MethodBase, MethodBase[]> calling = new Dictionary<MethodBase, MethodBase[]>();
        var assembly = typeof(StackOverflowDetector).Assembly;

        foreach (var type in assembly.GetTypes())
        {
            foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).OfType<MethodBase>())
            {
                var body = member.GetMethodBody();
                if (body!=null)
                {
                    var bytes = body.GetILAsByteArray();
                    if (bytes != null)
                    {
                        // Store all the calls of this method:
                        var calls = Decompiler.Decompile(member, bytes);
                        calling[member] = calls;
                    }
                }
            }
        }

        // Check every method:
        foreach (var method in calling.Keys)
        {
            // If method A -> ... -> method A, we have a possible infinite recursion
            CheckRecursion(method, calling, new HashSet<MethodBase>());
        }
    }

現在、メソッド サイクルに再帰が含まれているという事実は、スタック オーバーフローが発生することを保証するものではありません。これは、スタック オーバーフロー例外の最も可能性の高い前提条件です。要するに、これは、このコードがスタック オーバーフロー発生する可能性のあるコードの断片を特定することを意味し、ほとんどのコードを大幅に絞り込む必要があります。

さらに他のアプローチ

ここでは説明していませんが、他にも試すことができる方法がいくつかあります。

  1. CLR プロセスをホストして処理することにより、スタック オーバーフローを処理します。まだ「キャッチ」できないことに注意してください。
  2. すべての IL コードを変更し、別の DLL を構築し、再帰のチェックを追加します。はい、それはかなり可能です (私は過去に実装しました :-); ただ難しいだけでなく、それを正しく行うには多くのコードが必要です。
  3. .NET プロファイリング API を使用してすべてのメソッド呼び出しをキャプチャし、それを使用してスタック オーバーフローを把握します。たとえば、コール ツリーで同じメソッドが X 回発生した場合にシグナルを送信するというチェックを実装できます。有利なスタートを切るプロジェクトclrprofilerがあります。
于 2015-06-06T09:13:31.960 に答える
9

XmlWriter オブジェクトのラッパーを作成することをお勧めします。これにより、WriteStartElement/WriteEndElement への呼び出しの量がカウントされ、タグの量を特定の数 (fe 100) に制限すると、別の例外をスローできるようになります。たとえば、-操作が無効です。

ほとんどの場合、これで問題が解決するはずです

public class LimitedDepthXmlWriter : XmlWriter
{
    private readonly XmlWriter _innerWriter;
    private readonly int _maxDepth;
    private int _depth;

    public LimitedDepthXmlWriter(XmlWriter innerWriter): this(innerWriter, 100)
    {
    }

    public LimitedDepthXmlWriter(XmlWriter innerWriter, int maxDepth)
    {
        _maxDepth = maxDepth;
        _innerWriter = innerWriter;
    }

    public override void Close()
    {
        _innerWriter.Close();
    }

    public override void Flush()
    {
        _innerWriter.Flush();
    }

    public override string LookupPrefix(string ns)
    {
        return _innerWriter.LookupPrefix(ns);
    }

    public override void WriteBase64(byte[] buffer, int index, int count)
    {
        _innerWriter.WriteBase64(buffer, index, count);
    }

    public override void WriteCData(string text)
    {
        _innerWriter.WriteCData(text);
    }

    public override void WriteCharEntity(char ch)
    {
        _innerWriter.WriteCharEntity(ch);
    }

    public override void WriteChars(char[] buffer, int index, int count)
    {
        _innerWriter.WriteChars(buffer, index, count);
    }

    public override void WriteComment(string text)
    {
        _innerWriter.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        _innerWriter.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        _innerWriter.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        _innerWriter.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        _depth--;

        _innerWriter.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        _innerWriter.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        _innerWriter.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        _innerWriter.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        _innerWriter.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        _innerWriter.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        _innerWriter.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument(bool standalone)
    {
        _innerWriter.WriteStartDocument(standalone);
    }

    public override void WriteStartDocument()
    {
        _innerWriter.WriteStartDocument();
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        if (_depth++ > _maxDepth) ThrowException();

        _innerWriter.WriteStartElement(prefix, localName, ns);
    }

    public override WriteState WriteState
    {
        get { return _innerWriter.WriteState; }
    }

    public override void WriteString(string text)
    {
        _innerWriter.WriteString(text);
    }

    public override void WriteSurrogateCharEntity(char lowChar, char highChar)
    {
        _innerWriter.WriteSurrogateCharEntity(lowChar, highChar);
    }

    public override void WriteWhitespace(string ws)
    {
        _innerWriter.WriteWhitespace(ws);
    }

    private void ThrowException()
    {
        throw new InvalidOperationException(string.Format("Result xml has more than {0} nested tags. It is possible that xslt transformation contains an endless recursive call.", _maxDepth));
    }
}
于 2010-01-22T13:00:20.573 に答える
5

この回答は@WilliamJockusch向けです。

StackOverflowExceptions を追跡する一般的な方法があるかどうか疑問に思っています。言い換えれば、コードのどこかに無限再帰があるとしますが、どこにあるのかわかりません。それが起こっているのを見るまで、コードをあちこちにステップ実行するよりも簡単な方法で追跡したいと思います。私はそれがどれほどハックであるかは気にしません。たとえば、スタックの深さをポーリングし、「高すぎる」と考えられるレベルに達した場合に不平を言う、おそらく別のスレッドからでもアクティブ化できるモジュールがあれば素晴らしいでしょう。たとえば、「高すぎる」を 600 フレームに設定し、スタックが深すぎると問題になると考えます。そのようなことは可能ですか。もう 1 つの例は、コード内の 1000 回ごとのメソッド呼び出しをデバッグ出力に記録することです。これによりオーバーローの証拠が得られる可能性はかなり高く、出力がそれほど大きくなることはないでしょう。重要なのは、オーバーフローが発生している場所にチェックを書き込むことはできないということです。全体の問題は、それがどこにあるのかわからないことです。解決策は、私の開発環境がどのように見えるかに依存しないことが望ましいです。つまり、特定のツールセット (VS など) を介して C# を使用しているとは限りません。

この StackOverflow をキャッチするためのデバッグ テクニックを聞きたがっているようです。

1. メモリ ダンプ。

長所: メモリ ダンプは、スタック オーバーフローの原因を突き止める確実な方法です。AC# MVP と私は SO のトラブルシューティングに協力し、彼はそれについてのブログをここに投稿しました。

この方法は、問題を追跡するための最速の方法です。

この方法では、ログに表示される手順に従って問題を再現する必要はありません。

短所: メモリ ダンプは非常に大きく、プロセスにAdPlus /procdump をアタッチする必要があります。

2. アスペクト指向プログラミング。

長所: これはおそらく、アプリケーションのすべてのメソッドにコードを記述することなく、任意のメソッドからのコール スタックのサイズをチェックするコードを実装する最も簡単な方法です。呼び出しの前後にインターセプトできるAOP フレームワークがたくさんあります。

スタック オーバーフローの原因となっているメソッドを教えてくれます。

StackTrace().FrameCountアプリケーション内のすべてのメソッドの入口と出口をチェックできます。

短所:パフォーマンスに影響があります-フックはすべてのメソッドのILに埋め込まれており、実際に「非アクティブ化」することはできません。

開発環境のツール セットによって多少異なります。

3. ユーザー アクティビティのログ記録。

1 週間前、私は再現が困難な問題をいくつか突き止めようとしていました。この QA User Activity Logging, Telemetry (and Variables in Global Exception Handlers)を投稿しました。私が到達した結論は、未処理の例外が発生したときにデバッガーで問題を再現する方法を確認するための、非常に単純なユーザー アクション ロガーでした。

長所: 自由にオンまたはオフにすることができます (つまり、イベントにサブスクライブします)。

ユーザー アクションを追跡するために、すべてのメソッドをインターセプトする必要はありません。

AOP よりも簡単に、メソッドがサブスクライブされているイベントの数を数えることができます。

ログ ファイルは比較的小さく、問題を再現するために実行する必要があるアクションに焦点を当てています。

ユーザーがアプリケーションをどのように使用しているかを理解するのに役立ちます。

短所: Windows サービスには適していません。このような Web アプリ用のより優れたツールがあると確信しています

スタック オーバーフローの原因となるメソッドを必ずしも教えてくれるとは限りません。

問題を取得してすぐにデバッグできるメモリ ダンプではなく、問題を手動で再現するログをステップ実行する必要があります。

 


上記のすべてのテクニックと @atlaste が投稿したいくつかのテクニックを試して、PROD 環境で実行するのに最も簡単/最速/ダーティ/最も許容できるものを教えてください。

とにかく、このSOを追跡して頑張ってください。

于 2015-06-06T10:22:50.053 に答える
3

アプリケーションがサードパーティ コード (Xsl スクリプト) に依存している場合は、最初にそれらのバグから防御するかどうかを決定する必要があります。本当に防御したい場合は、別の AppDomains で外部エラーが発生しやすいロジックを実行する必要があると思います。StackOverflowException をキャッチするのは良くありません。

この質問も確認してください。

于 2010-09-01T14:40:33.497 に答える
2

今日はスタックオーバーフローがあり、あなたの投稿をいくつか読んで、ガベージ コレクターを支援することにしました。

以前は、次のようなほぼ無限ループがありました。

    class Foo
    {
        public Foo()
        {
            Go();
        }

        public void Go()
        {
            for (float i = float.MinValue; i < float.MaxValue; i+= 0.000000000000001f)
            {
                byte[] b = new byte[1]; // Causes stackoverflow
            }
        }
    }

代わりに、次のようにリソースをスコープ外に実行させます。

class Foo
{
    public Foo()
    {
        GoHelper();
    }

    public void GoHelper()
    {
        for (float i = float.MinValue; i < float.MaxValue; i+= 0.000000000000001f)
        {
            Go();
        }
    }

    public void Go()
    {
        byte[] b = new byte[1]; // Will get cleaned by GC
    }   // right now
}

それは私のために働いた、それが誰かを助けることを願っています。

于 2013-07-09T16:22:20.217 に答える
2

.NET 4.0 ではHandleProcessCorruptedStateExceptions、System.Runtime.ExceptionServices からの属性を、try/catch ブロックを含むメソッドに追加できます。これは本当にうまくいきました!推奨されていないかもしれませんが、動作します。

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.ExceptionServices;

namespace ExceptionCatching
{
    public class Test
    {
        public void StackOverflow()
        {
            StackOverflow();
        }

        public void CustomException()
        {
            throw new Exception();
        }

        public unsafe void AccessViolation()
        {
            byte b = *(byte*)(8762765876);
        }
    }

    class Program
    {
        [HandleProcessCorruptedStateExceptions]
        static void Main(string[] args)
        {
            Test test = new Test();
            try {
                //test.StackOverflow();
                test.AccessViolation();
                //test.CustomException();
            }
            catch
            {
                Console.WriteLine("Caught.");
            }

            Console.WriteLine("End of program");

        }

    }      
}
于 2010-08-25T09:54:02.593 に答える
1

@WilliamJockusch、あなたの懸念を正しく理解していれば、停止問題を解決することを意味するため、(数学的な観点から)無限再帰を常に識別することはできません。それを解決するには、超再帰アルゴリズム(たとえば、試行錯誤の述語など) またはハイパーコンピューティングが可能なマシン(この本の次のセクションで説明されています- プレビューとして利用可能) が必要です。

実用的な観点からは、次のことを知っておく必要があります。

  • 特定の時間に残っているスタック メモリの量
  • 特定の出力のために特定の時間に再帰メソッドが必要とするスタック メモリの量。

現在のマシンでは、このデータはマルチタスクのために非常に可変であり、そのタスクを実行するソフトウェアについては聞いたことがありません。

何か不明な点があればお知らせください。

于 2015-06-12T08:30:06.460 に答える
0

一見すると、別のプロセスを開始することを除けば、 を処理する方法はないようですStackOverflowException。他の誰かが尋ねる前に、私は を使ってみAppDomainましたが、うまくいきませんでした:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

namespace StackOverflowExceptionAppDomainTest
{
    class Program
    {
        static void recrusiveAlgorithm()
        {
            recrusiveAlgorithm();
        }
        static void Main(string[] args)
        {
            if(args.Length>0&&args[0]=="--child")
            {
                recrusiveAlgorithm();
            }
            else
            {
                var domain = AppDomain.CreateDomain("Child domain to test StackOverflowException in.");
                domain.ExecuteAssembly(Assembly.GetEntryAssembly().CodeBase, new[] { "--child" });
                domain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) =>
                {
                    Console.WriteLine("Detected unhandled exception: " + e.ExceptionObject.ToString());
                };
                while (true)
                {
                    Console.WriteLine("*");
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

ただし、個別プロセス ソリューションを使用することになった場合は、エラーを自分で使用Process.Exitedして処理し、ユーザー エクスペリエンスを向上させることをお勧めします。Process.StandardOutput

于 2015-06-11T13:47:43.830 に答える
-2

このプロパティは、数回の呼び出しごとに読み取ることができEnvironment.StackTrace、スタックトレースが事前設定した特定のしきい値を超えた場合は、関数を返すことができます。

また、いくつかの再帰関数をループに置き換えてみてください。

于 2012-12-27T18:18:10.683 に答える