43

概要:

デバッグできないように見えるアプリケーションで、定期的に.NET FatalExecutionEngineエラーが発生します。表示されるダイアログは、プログラムを閉じるか、エラーに関する情報をMicrosoftに送信することのみを提供します。もっと詳しい情報を見てみましたが、使い方がわかりません。

エラー:

エラーは、アプリケーションの下のイベントビューアに表示され、次のようになります。

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

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

応用:

アプリケーションは、VS2008を使用してC#/。NET 3.5で記述され、セットアッププロジェクトを介してインストールされます。

アプリはマルチスレッドであり、System.Net.HttpWebRequestとそのメソッドを使用して複数のWebサーバーからデータをダウンロードします。.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関連のアイテム。

4

5 に答える 5

45

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

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

しかし、質問から判断すると、マネージコードを使用しています。ほとんどの場合コードは管理されています。しかし、あなたは多くのアンマネージコードを実行しています。HttpWebRequestを実際に機能させるすべての低レベルコードは管理されていません。また、CLRもC ++で記述されているため、技術的にはヒープが破損する可能性があります。しかし、それを4000以上改訂し、何百万ものプログラムがそれを使用した後でも、それがまだヒープクーティーに苦しんでいる可能性は非常に小さいです。

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

問題を特定します。問題の原因は自分のコードでもMicrosoftのコードでもないと想定します。それが最初に環境であると仮定して、クラップウェアを取り除きます。

壮大な環境FEEEストーリーについては、このスレッドをお読みください。

于 2010-06-20T23:17:07.847 に答える
9

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

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

今詳細なバージョン

社内で開発され、C++で記述されたアンマネージDLLを使用しています。私自身のGUI開発はC#.Net4.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。今、私のページの多くにとって、これは大したことではありませんでした。彼らはメモリフットプリントが小さく、それほど多くは起こっていませんでした。しかし、これらの他の多くのページには、説明のために非常に詳細な大きなグラフィックがいくつかありました。私たちの機器のオペレーターによるこのインターフェースの通常の使用がメモリの膨大な割り当てを引き起こし、それがクリアされることはなく、最終的にマシン上のすべてのプロセスを詰まらせるまで、それほど長くはありませんでした。津波から海嘯へと初期開発のラッシュが収まった後、私はついにメモリリークに完全に取り組むことにしました。メモリをクリーンアップするために実装したすべてのトリックの詳細については説明しません(WeakReferenceを画像に、Unload()でイベントハンドラーをフック解除し、IWeakEventListenerを実装するカスタムタイマーを使用します)インターフェイスなど)。私が行った主な変更は、オブジェクトの代わりにUriを使用してページに移動することでした(NavigationService.Navigateメソッド(Uri))。このタイプのナビゲーションを使用する場合、2つの重要な違いがあります。

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

だから今私のナビゲーションは次のようになります:

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

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

すべてがうまくいったようです。グラフィックを多用するページをナビゲートすると、この特定のページにアクセスしてdataSource dllを呼び出し、いくつかのコンボボックスに入力するまで、メモリはすぐに初期状態近くまでクリーンアップされました。それから私はこの厄介なものを手に入れましたFatalEngineExecutionError。何日にもわたって調査し、漠然とした提案、または私には当てはまらない非常に具体的な解決策を見つけ、個人的なプログラミング兵器のほぼすべてのデバッグ兵器を解き放った後、私は最終的にこれを釘付けにする唯一の方法を決定しましたダウンは、この特定のページの正確なコピーを、要素ごと、メソッドごと、行ごとに再構築するという極端な手段でしたが、最終的にこの例外をスローするコードに出くわしました。それは私が示唆しているのと同じくらい退屈で苦痛でした、しかし私はついにそれを突き止めました。

管理されていない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つあります。私が割り当てる配列サイズは3ではなく2です。これを行ったのは、この特定のComboBoxには、すべて3つの選択項目があるページ上の他のComboBoxとは対照的に、2つの選択項目しかないことがわかっていたためです。しかし、アンマネージコードは私が見たように物事を見ることができませんでした。渡した配列を取得し、サイズ3の配列をサイズ2の割り当てに書き込もうとしました。それだけです。*バン!* *クラッシュ!*割り当てサイズを3に変更したところ、エラーはなくなりました。

現在、この特定のコードは、少なくとも1年間、このエラーなしですでに実行されていました。Uriしかし、とは対照的に、を介してこのページに移動するという単純な行為Objectにより、クラッシュが表示されました。これは、私が使用したナビゲーション方法のために、最初のオブジェクトを別の方法で割り当てる必要があることを意味します。私の古いナビゲーション方法では、記憶はちょうどその場に積み上げられ、永遠にふさわしいと思ったので、それが1つか2つの小さな場所で少し壊れていても問題ではなかったようです。ガベージコレクターが実際にそのメモリで何かを実行する必要があると(クリーンアップなど)、メモリの破損を検出して例外をスローしました。皮肉なことに、私の主要なメモリリークは、致命的なメモリエラーを隠蔽することでした。

明らかに、将来このようなクラッシュを引き起こすような単純な仮定を回避するために、このインターフェイスを確認します。これが、他の人が自分のコードで何が起こっているのかを知るのに役立つことを願っています。

于 2014-08-27T09:52:06.147 に答える
3

この種の問題をどこから始めればよいかについての素晴らしいチュートリアルになるかもしれないプレゼンテーションはこれです:IngoRammerによる.NETでのハードコアプロダクションデバッグ

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

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

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

于 2010-06-23T06:46:24.250 に答える
2

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

しかし、構成エラーに続いて、ロガーは失敗し始め、ハンドラー自体がスローされていたためFatalExecutionEngineError、どこからともなく来たように見えました。

したがって、このエラーが発生FirstChanceExceptionした場合は、コード内の任意の場所で発生するものを検索するのに数秒を費やすことができ、頭をかきむしる時間を数時間節約できます:)

于 2017-09-18T14:36:57.063 に答える
-1

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

于 2013-01-20T19:54:06.943 に答える