質問

まとめ:

デバッグできないと思われるアプリケーションで、.NET Fatal Execution Engine Error が定期的に発生します。表示されるダイアログでは、プログラムを閉じるか、エラーに関する情報を Microsoft に送信するかだけが提案されます。詳しい情報を見てみましたが、どう活用すればいいのか分かりません。

エラー:

このエラーはイベント ビューアの [アプリケーション] に表示され、次のとおりです。

.NETランタイムバージョン2.0.50727.3607-致命的な実行エンジンエラー(7A09795E)(80131506)

実行しているコンピュータは Windows XP Professional SP 3 です。(Intel Core2Quad Q6600 2.4GHz、2.0 GB の RAM) マルチスレッド ダウンロード (以下を参照) が機能しない他の .NET ベースのプロジェクトは問題なく動作するようです。

応用:

このアプリケーションは、VS2008 を使用して C#/.NET 3.5 で作成され、セットアップ プロジェクトを通じてインストールされます。

アプリはマルチスレッドであり、次を使用して複数の Web サーバーからデータをダウンロードします。 System.Net.HttpWebRequest およびその方法。.NET エラーがスレッドまたは HttpWebRequest に関係していると判断しましたが、この特定のエラーはデバッグが不可能と思われるため、これ以上詳しく調べることができませんでした。

Program.cs の次のようなエラーをさまざまなレベルで処理しようとしました。

// handle UI thread exceptions
Application.ThreadException += Application_ThreadException;

// handle non-UI thread exceptions
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// force all windows forms errors to go through our handler
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

その他のメモと試したこと...

  • Visual Studio 2008 をターゲット マシンにインストールし、デバッグ モードで実行しようとしましたが、依然としてエラーが発生し、ソース コードのどこでエラーが発生したかについてのヒントはありません。
  • インストールされているバージョン (リリース) からプログラムを実行すると、エラーがより頻繁に発生します。通常、アプリケーションの起動後数分以内に発生します。VS2008 内でプログラムをデバッグ モードで実行すると、エラーが生成されるまでに数時間または数日間実行される可能性があります。
  • .NET 3.5を再インストールし、すべての更新が適用されていることを確認しました。
  • イライラしてランダムなキュービクルオブジェクトを壊しました。
  • 例外をキャッチしてログに記録するために、スレッドとダウンロードを処理するコードの一部が書き直されましたが、ログを記録すると問題がさらに悪化するようでした (データはまったく提供されませんでした)。

質問:

この種のエラーをトラブルシューティングまたはデバッグするにはどのような手順を実行できますか?次のステップはメモリダンプなどのようですが、それを解釈するのは経験がありません。おそらく、エラーをキャッチするためにコードでできることは他にもあるでしょう...「致命的な実行エンジン エラー」がもっと有益であれば良いのですが、インターネットで検索したところ、これは多くの .NET 関連項目で一般的なエラーであることがわかりました。

役に立ちましたか?

解決

さて、あなたには大きな問題があります。この例外は、ガベージ コレクションされたヒープの整合性が侵害されたことを検出したときに CLR によって発生します。ヒープの破損は、C や C++ などのアンマネージ言語でコードを書いたことのあるプログラマーにとって悩みの種です。

これらの言語はそれを可能にします とても ヒープは簡単に破損しますが、必要なのは、ヒープ上に割り当てられた配列の末尾を超えて書き込むことだけです。または、解放後にメモリを使用します。または、ポインタの値が不正です。マネージ コードが解決するために発明された種類のバグ。

しかし、質問から判断すると、マネージドコードを使用しています。まあ、ほとんどの場合、 あなたの コードが管理されています。でもあなたは実行している たくさん アンマネージ コードの。HttpWebRequest を実際に機能させる低レベル コードはすべて管理対象外です。CLR も同様で、C++ で書かれているため、技術的にはヒープを破損する可能性が高くなります。しかし、4,000 を超える改訂が行われ、何百万ものプログラムがそれを使用した後でも、依然としてヒープ問題に悩まされる可能性は高くなります。 とても 小さい。

HttpWebRequest の一部を必要とする他のすべてのアンマネージ コードには同じことが当てはまりません。あなたが書いたものではなく、Microsoft によって文書化されていないため、あなたが知らないコード。ファイアウォール。ウイルス スキャナー。会社のインターネット使用状況モニター。主は誰の「ダウンロード アクセラレータ」を知っていますか。

問題の原因がユーザーのコードでも Microsoft のコードでもないものとして、問題を切り分けます。環境第一だと考えて、クラップウェアを取り除きましょう。

環境手数料に関する壮大なストーリーについては、こちらをお読みください。 このスレッド.

他のヒント

これまでの提案は本質的にかなり一般的なものであるため、特定のコード例、この例外を発生させるために実装した背景の変更、およびそれを解決した方法を使用して、この例外に対する私自身の戦いを投稿するのに役立つかもしれないと考えました。

まずはショートバージョンから:私は C++ で書かれた社内 DLL (アンマネージド) を使用していました。.NET 実行可能ファイルから特定のサイズの配列を渡しました。アンマネージ コードは、マネージ コードによって割り当てられていない配列の場所に書き込もうとしました。これにより、メモリ内で破損が発生し、後でガベージ コレクションが行われるように設定されました。ガベージ コレクターはメモリを収集する準備をするときに、まずメモリのステータス (および境界) をチェックします。破損が見つかった場合、 ブーム.

TL;DR バージョン:

私は社内で開発され、C++ で書かれたアンマネージ DLL を使用しています。私自身の GUI 開発は C# .Net 4.0 で行っています。さまざまなアンマネージ メソッドを呼び出しています。この DLL は事実上データ ソースとして機能します。DLL からの extern 定義の例:

    [DllImport(@"C:\Program Files\MyCompany\dataSource.dll",
        EntryPoint = "get_sel_list",
        CallingConvention = CallingConvention.Winapi)]
    private static extern int ExternGetSelectionList(
        uint parameterNumber,
        uint[] list,
        uint[] limits,
        ref int size);

次に、プロジェクト全体で使用できるようにメソッドを独自のインターフェイスにラップします。

    /// <summary>
    /// Get the data for a ComboBox (Drop down selection).
    /// </summary>
    /// <param name="parameterNumber"> The parameter number</param>
    /// <param name="messageList"> Message number </param>
    /// <param name="valueLimits"> The limits </param>
    /// <param name="size"> The maximum size of the memory buffer to 
    /// allocate for the data </param>
    /// <returns> 0 - If successful, something else otherwise. </returns>
    public int GetSelectionList(uint parameterNumber, 
           ref uint[] messageList, 
           ref uint[] valueLimits, 
           int size)
    {
        int returnValue = -1;
        returnValue = ExternGetSelectionList(parameterNumber,
                                         messageList, 
                                         valueLimits, 
                                         ref size);
        return returnValue;
    }

このメソッドの呼び出し例:

            uint[] messageList = new uint[3];
            uint[] valueLimits = new uint[3];
            int dataReferenceParameter = 1;

            // BUFFERSIZE = 255.
            MainNavigationWindow.MainNavigationProperty.DataSourceWrapper.GetSelectionList(
                          dataReferenceParameter, 
                          ref messageList, 
                          ref valueLimits, 
                          BUFFERSIZE);

GUI では、さまざまなグラフィックスやユーザー入力を含むさまざまなページ間を移動します。前の方法では、データを取得して設定することができました ComboBoxes. 。この例外が発生する前のナビゲーションのセットアップと呼び出しの例:

ホスト ウィンドウでプロパティを設定します。

    /// <summary>
    /// Gets or sets the User interface page
    /// </summary>
    internal UserInterfacePage UserInterfacePageProperty
    {
        get
        {
            if (this.userInterfacePage == null)
            {
                this.userInterfacePage = new UserInterfacePage();
            }

            return this.userInterfacePage;
        }

        set { this.userInterfacePage = value; }
    }

次に、必要に応じて、次のページに移動します。

MainNavigationWindow.MainNavigationProperty.Navigate(
        MainNavigation.MainNavigationProperty.UserInterfacePageProperty);

すべてが十分にうまく機能しましたが、いくつかの深刻な問題が発生しました。オブジェクトを使用してナビゲートするとき (NavigationService.Navigate メソッド (オブジェクト))、デフォルト設定 IsKeepAlive 財産は true. 。しかし、問題はそれよりもさらに悪質です。を設定しても、 IsKeepAlive そのページのコンストラクター内の値を具体的に指定する false, 、ガベージコレクターによってまるで放置されたままになっています。 true. 。私のページの多くでは、これは大したことではありませんでした。メモリ使用量は小さく、それほど多くの処理は行われませんでした。しかし、これらの他のページには、説明目的で非常に詳細な大きなグラフィックスがいくつか掲載されていました。機器のオペレーターがこのインターフェイスを通常に使用すると、クリアされない大量のメモリ割り当てが発生し、最終的にはマシン上のすべてのプロセスが詰まるまで、それほど時間はかかりませんでした。初期開発のラッシュが津波からさらに干潮にまで落ち着いた後、私はついにメモリ リークにきっぱりと取り組むことにしました。メモリをクリーンアップするために私が実装したすべてのトリックの詳細については説明しません (弱い参照を画像に追加し、Unload() でイベント ハンドラーのフックを解除し、 IWeakEventListener インターフェイスなど...)。私が行った主な変更は、オブジェクト (NavigationService.Navigate メソッド (Uri))。このタイプのナビゲーションを使用する場合、次の 2 つの重要な違いがあります。

  1. IsKeepAlive に設定されています false デフォルトでは。
  2. ガベージ コレクターは、次のようにナビゲーション オブジェクトをクリーンアップしようとします。 IsKeepAlive に設定されました false.

したがって、私のナビゲーションは次のようになります。

MainNavigation.MainNavigationProperty.Navigate(
    new Uri("/Pages/UserInterfacePage.xaml", UriKind.Relative));

ここで他に注意すべき点があります:これは、ガベージ コレクターによるオブジェクトのクリーンアップ方法に影響するだけでなく、オブジェクトのクリーンアップ方法にも影響します。 最初にメモリに割り当てられた, 、すぐにわかります。

すべてがうまくいったように見えました。グラフィックスを多用するページをナビゲートすると、私のメモリはすぐに初期状態近くまでクリーンアップされ、いくつかのコンボボックスを埋めるために dataSource dll への特定の呼び出しでこの特定のページに到達するまで続きました。そしたらこんなひどいことになった FatalEngineExecutionError. 。何日もかけて調査し、漠然とした提案や自分には当てはまらない非常に具体的な解決策を見つけ、個人的なプログラミングの武器庫にあるほぼすべてのデバッグ武器を解放した後、私は最終的に、これを本当に成功させる唯一の方法であると決心しました。 down は、最終的にこの例外をスローするコードに遭遇するまで、この特定のページの正確なコピーを要素ごと、メソッドごと、行ごとに再構築する極端な手段でした。私がほのめかしているのと同じくらい退屈で苦痛でしたが、最終的にそれを突き止めました。

これは、データを入力するために送信した配列にデータを書き込むためにアンマネージ DLL がメモリを割り当てている方法に問題があることが判明しました。その特定のメソッドは実際にパラメータ番号を調べ、その情報に基づいて、送信した配列に書き込むことが予想されるデータ量に基づいて特定のサイズの配列を割り当てます。クラッシュしたコード:

            uint[] messageList = new uint[2];
            uint[] valueLimits = new uint[2];
            int dataReferenceParameter = 1;

            // BUFFERSIZE = 255.
            MainNavigationWindow.MainNavigationProperty.DataSourceWrapper.GetSelectionList(
                           dataReferenceParameter, 
                           ref messageList, 
                           ref valueLimits, 
                           BUFFERSIZE);

このコードは上記のサンプルと同じように見えるかもしれませんが、小さな違いが 1 つあります。私が割り当てる配列サイズは 2 ない 3. 。このようにしたのは、ページ上の他のコンボボックスにはすべて 3 つの選択項目があるのに対し、この特定の ComboBox には 2 つの選択項目しかないことがわかっていたからです。ただし、アンマネージ コードでは、私が見たように物事が見えませんでした。私が渡した配列を取得し、 size[ 3 ] 配列を size[ 2 ] 割り当てに書き込もうとしました。それだけで済みました。* バン! * * クラッシュ! ※割り当てサイズを3に変更したらエラーはなくなりました。

この特定のコードは、少なくとも 1 年間はこのエラーが発生することなく実行されていました。しかし、 Uri とは対照的に Object クラッシュが発生する原因となりました。これは、使用したナビゲーション方法により、初期オブジェクトを別の方法で割り当てる必要があることを意味します。私の古いナビゲーション方法では、メモリは単に所定の位置に積み上げられ、永遠に私が適切だと思うように放置されていたため、1 つまたは 2 つの小さな場所で多少の破損があったとしても、問題ではないようでした。ガベージ コレクターがそのメモリに対して実際に何かを行う必要があると (クリーンアップなど)、メモリ破損を検出して例外をスローしました。皮肉なことに、 重大なメモリ リークが致命的なメモリ エラーを隠していたのです。

明らかに、将来このような単純な仮定がクラッシュを引き起こすことを避けるために、このインターフェースを見直すつもりです。これが、他の人が自分のコードで何が起こっているのかを知る手助けになれば幸いです。

この種の問題をどこから始めればよいかについての優れたチュートリアルとなるプレゼンテーションは次のとおりです。 Ingo Rammer による .NET でのハードコアな本番環境のデバッグ.

私は C++/CLI コーディングを少しだけ行っていますが、ヒープの破損によってこのエラーが発生することは通常ありません。通常、ヒープ破損はデータ破損とその後の通常例外、またはメモリ保護エラーを引き起こしますが、これにはおそらく何も意味がありません。

.net 4.0 (アンマネージ コードのロード方法が異なる) を試すことに加えて、CLR の x86 エディションと x64 エディションを比較する必要があります (可能であれば)。x64 バージョンの方がアドレス空間が大きいため、malloc (+フラグメンテーション) の動作が完全に異なります。運が良ければ、別の(よりデバッグ可能な)エラーが発生する可能性があります(発生した場合)。

また、Visual Studio をオンにして実行するときに、デバッガー (プロジェクト オプション) でアンマネージ コードのデバッグをオンにしましたか?マネージド デバッグ アシスタントはオンになっていますか?

私の場合、例外ハンドラーをインストールしました AppDomain.CurrentDomain.FirstChanceException. 。このハンドラーはいくつかの例外をログに記録していましたが、数年間はすべて問題ありませんでした (実際には、このデバッグ コードは運用環境に残るべきではありませんでした)。

しかし、構成エラーの後、ロガーが失敗し始め、ハンドラー自体がスローされ、その結果、明らかに FatalExecutionEngineError どこからともなくやってくるようです。

そのため、このエラーが発生した場合は、次のエラーが発生したかどうかを検索するのに数秒かかる可能性があります。 FirstChanceException コードのどこにでも追加できるので、数時間頭を悩ませる手間が省けるかもしれません :)

thread.sleep() を使用している場合、それが原因である可能性があります。アンマネージ コードは、kernell.32 sleep() 関数からのみスリープできます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top