【问题标题】:Application.Exit() which operation is firstApplication.Exit() 哪个操作是第一个
【发布时间】:2012-07-18 21:40:15
【问题描述】:

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

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

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

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

【问题讨论】:

  • 为什么你认为 WM_QUIT 消息不会被处理?
  • 更好的问题是,“由于这种操作顺序问题,您遇到了什么问题?”
  • QUESTION 中没有一个问号...
  • @IcyBrk 你真的应该将你的答案作为评论发布,但别担心,我在下面的帖子中添加了一个关于 ExitThreadCore 的 sn-p。

标签: c# .net winforms


【解决方案1】:

有趣的问题;使用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 可能永远不会触发。作为此批发终止的一部分,它还将 [我犹豫使用尝试,因为我从未见过它失败] 杀死任何线程,包括消息循环正在运行的“主”线程。

【讨论】:

  • +1 不错的侦探。您的“流氓线程”让进程保持打开状态的原因是它不是在后台运行,它是一个前台线程。在所有前台线程终止之前,该进程无法终止。这就是为什么你应该将你的“背景”线程IsBackground属性设置为true
  • @Tergiver 感谢您的澄清,非常感谢。
  • 谢谢dash,你澄清了事实,谢谢你的详细挖掘,我还有一个疑问,你能告诉我ExitThreadCore()到底是做什么的吗,我试着阅读源代码使用反射器,但不是很成功。
猜你喜欢
  • 2012-01-28
  • 2014-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-24
  • 2023-03-18
  • 1970-01-01
  • 2022-06-16
相关资源
最近更新 更多