Application.Exit(), первая операция

Когда я прочитал документы в MSDN для Application.Exit(), он говорит:

Сообщает все насосы сообщений, которые они должны завершить, а затем закрывает все окна приложений после обработки сообщений.

В моем понимании, чтобы сообщить о завершении всех сообщений, этот метод, наконец, отправит сообщение WM_QUIT в очередь сообщений приложения. И после размещения сообщения метод затем закроет каждое окно (по MSDN). Проблема возникает здесь, когда этот метод пытается закрыть каждое окно, сообщение WM_QUIT должно быть не обработано, но MSDN сказал, что "он закрывает все окна после, обработанные сообщениями".

Кажется, что документация противоречит моему умозаключению. В чем здесь проблема, любая помощь приветствуется.

Ответ 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 do?

Ну, он не убивает поток напрямую, но он запускает процесс:

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, вероятно, никогда не загорится, например. В рамках этого оптового расторжения также будет [я смущаюсь использовать попытку, поскольку я никогда не видел ее сбой) убивают любые потоки, включая "основной" поток, в котором работает цикл сообщений.