Application.Exit()哪个操作是第一个

时间:2012-07-18 21:40:16

标签: c# .net winforms

当我在MSDN上阅读Application.Exit()的文档时,它说:

通知所有消息泵必须终止,然后在处理完消息后关闭所有应用程序窗口。

根据我的理解,为了通知所有消息泵终止,这个方法最终会将WM_QUIT消息发送到应用程序消息队列。发布消息后,该方法将关闭每个窗口(通过MSDN)。问题出现在这里,当这个方法试图关闭每个窗口时,WM_QUIT消息应该没有被处理,但MSDN说“它关闭所有窗口 消息后已被处理“。

似乎文档与我的推论相矛盾。这里有什么问题,非常感谢任何帮助。

1 个答案:

答案 0 :(得分: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实际上不会导致该线程   终止。这些方法引发了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);

它有效地呼吁操作系统终止进程;这会以很少的优雅终止所有窗户;例如,OnFormClosing可能永远不会被解雇。作为批发终止的一部分,它[我会犹豫使用尝试,因为我从未见过它失败]杀死任何线程,包括消息循环正在运行的'main'线程。