10

Application.Exit()のMSDNのドキュメントを読むと、次のように表示されます。

すべてのメッセージポンプに終了する必要があることを通知し、メッセージが処理された後、すべてのアプリケーションウィンドウを閉じます。

私の理解では、すべてのメッセージポンプに終了を通知するために、このメソッドは最終的にWM_QUITメッセージをアプリケーションメッセージキューに送信します。そして、メッセージを投稿した後、メソッドは各ウィンドウを閉じます(MSDNによる)。ここで問題が発生します。このメソッドが各ウィンドウを閉じようとすると、WM_QUITメッセージは処理されないはずですが、MSDNは「メッセージが処理されたにすべてのウィンドウを閉じる」と述べています。

ドキュメントは私の推論と矛盾しているようです。ここでの問題は何ですか、どんな助けでも大歓迎です。

4

1 に答える 1

14

興味深い質問; ILSpyを使用して、何をするかを見てみましょうApplication.Exit()

重要な方法はExitInternal

private static bool ExitInternal()
{
    bool flag = false;
    lock (Application.internalSyncObject)
    {
        if (Application.exiting)
        {
            return false;
        }
        Application.exiting = true;
        try
        {
            if (Application.forms != null)
            {
                foreach (Form form in Application.OpenFormsInternal)
                {
                    if (form.RaiseFormClosingOnAppExit())
                    {
                        flag = true;
                        break;
                    }
                }
            }
            if (!flag)
            {
                if (Application.forms != null)
                {
                    while (Application.OpenFormsInternal.Count > 0)
                    {
                        Application.OpenFormsInternal[0].RaiseFormClosedOnAppExit();
                    }
                }
                Application.ThreadContext.ExitApplication();
            }
        }
        finally
        {
            Application.exiting = false;
        }
    }
    return flag;
}

すべてがうまくいけば、アプリケーションは最初にすべてのフォームを閉じ、次に見逃したフォームをすべて閉じ、最後に呼び出しますApplication.ThreadContext.ExitApplication();

ExitApplicationの一部として、クリーンアップが表示されます。

private static void ExitCommon(bool disposing)
{
    lock (Application.ThreadContext.tcInternalSyncObject)
    {
        if (Application.ThreadContext.contextHash != null)
        {
            Application.ThreadContext[] array = new Application.ThreadContext[Application.ThreadContext.contextHash.Values.Count];
            Application.ThreadContext.contextHash.Values.CopyTo(array, 0);
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i].ApplicationContext != null)
                {
                    array[i].ApplicationContext.ExitThread();
                }
                else
                {
                    array[i].Dispose(disposing);
                }
            }
        }
    }
}

// System.Windows.Forms.ApplicationContext
/// <summary>Terminates the message loop of the thread.</summary>
/// <filterpriority>1</filterpriority>
public void ExitThread()
{
    this.ExitThreadCore();
}

ExitThreadCoreは何をしますか?

まあ、それはスレッドを直接殺しませんが、それはプロセスを開始します:

ExitThreadとExitThreadCoreは、実際にはスレッドを終了させません。これらのメソッドは、ApplicationオブジェクトがリッスンするThreadExitイベントを発生させます。次に、Applicationオブジェクトはスレッドを終了します。

しかし、本当に興味深いビットはで起こるようですarray[i].Dispose(disposing)

この方法の一部として、次のようになります。

if (this.messageLoopCount > 0 && postQuit)
{
    this.PostQuit();
}

PostQuit()はWM_QUITメッセージを送信するものです。そのため、いつ呼び出されるかについても考慮する必要Application.ThreadContext.Disposeがあります。フォームが閉じられた後のようですが、そこで修正されてうれしいです。

したがって、注文はすべてのフォームを閉じているように見えます。次に、WM_QUITメッセージを送信します。私はあなたが正しいと思います、ドキュメントは実際に間違った順序でイベントを持っているかもしれません...

また、私たちがよく目にする別の副作用も確認しています。アプリケーションが閉じられていても、バックグラウンドで実行されているスレッドがまだある場合、exeは実行中のアプリケーションのリストに残ります。フォームは閉じられましたが、Exit()が完了しないようにハミングしながら、その不正なスレッドがまだあります。

Tergiverが言及しているように:

スレッドは、バックグラウンドスレッドまたはフォアグラウンドスレッドのいずれかです。バックグラウンドスレッドは、バックグラウンドスレッドがプロセスの終了を妨げないことを除いて、フォアグラウンドスレッドと同じです。プロセスに属するすべてのフォアグラウンドスレッドが終了すると、共通言語ランタイムはプロセスを終了します。残りのバックグラウンドスレッドはすべて停止され、完了しません。

Thread.IsBackgroundThreadから)

私はまた何をするのか疑問に思いましたEnvironment.Exit

[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport("QCall", CharSet = CharSet.Unicode)]
internal static extern void _Exit(int exitCode);

プロセスを強制終了するためにOSを効果的に呼び出します。これにより、すべてのウィンドウがほとんど猶予なしで終了します。たとえば、OnFormClosingが起動することはおそらくないでしょう。この大規模な終了の一環として、メッセージループが実行されている「メイン」スレッドを含むすべてのスレッドを強制終了します[失敗するのを見たことがないので、試行を使用することを躊躇します]。

于 2012-07-18T21:57:58.513 に答える