Закрыть программу по событию от класса

Уже задавал подобный вопрос на старом форуме, но ответа не получил.
Есть массив из моих любимых UserControlов. Предполагается, что юзер должен иметь возможность закрывать каждый из них.
кнопка закрытия:


        private void btnClose_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {  
                if (e.X >= 0 && e.X <= btnClose.Width && e.Y >= 0 && e.Y <= btnClose.Height)
                {
                    Closing?.Invoke(this); //событие
                    Dispose();
                }
            }
        }

Обработчик:

            private void CloseHandler(object sender) =>
            {
                ZeratoolPlayerGui playerGui = sender as ZeratoolPlayerGui;
                players.Remove(playerGui);
                if (playerGui == activePlayer)
                {
                    if (players.Count > 0)
                    {
                        players[0].Activate();
                    }
                    else
                    {
                        activePlayer = null;
                    }
                }
                System.Diagnostics.Debug.WriteLine($"Player {playerGui.Title} closed");
            };

Это работает. Всё нормально (вроде как).
Но что если надо по этому событию закрыть всю программу?

            private void CloseHandler(object sender) =>
            {
                ZeratoolPlayerGui playerGui = sender as ZeratoolPlayerGui;
                players.Remove(playerGui);
                if (playerGui == activePlayer)
                {
                    if (players.Count > 0)
                    {
                        players[0].Activate();
                    }
                    else
                    {
                        activePlayer = null;
                    }
                }
                System.Diagnostics.Debug.WriteLine($"Player {playerGui.Title} closed");
                if (playerGui.IsFullscreen) <<добавлено
                {
                    Close();
                }
            };

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            config.Save();
            Hide();
            foreach (ZeratoolPlayerGui z in players)
            {
                z.Stop();
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (activePlayer != null && activePlayer.IsMaximized)
            {
                if (File.Exists(config.playlistFileName))
                {
                    File.Delete(config.playlistFileName);
                }
                if (activePlayer.Playlist.Count > 0)
                {
                    activePlayer.Playlist.SaveToFile(config.playlistFileName);
                }
            }

            foreach (ZeratoolPlayerGui z in players)
            {
                z.Dispose();
            }
            players.Clear();
        }

Вроде тоже не крашится. Но получается, что программа закроется, а код в методе btnClose_MouseUp() не выполнится до конца? Или что с ним станет? :thinking:

Так добавьте вывод и узнаете что выполняется )

Бывает, что некоторые вещи на самом деле выполняются чуть позже, после завершения всех текущих обработчиков событий.
Если тут не тот случай, то можно самому например создать таймер с задержкой 0 или BeginInvoke.

Я не про то.

                    Closing?.Invoke(this); //обработчик этого события диспозит текущий экземпляр и закрывает всю программу
                    Dispose(); //а с этим тогда что будет?

Дык я и говорю, добавьте везде вывод и узнаете.

        private void btnClose_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {  
                if (e.X >= 0 && e.X <= btnClose.Width && e.Y >= 0 && e.Y <= btnClose.Height)
                {
                    Closing?.Invoke(this);
                    System.Diagnostics.Debug.WriteLine("before dispose");
                    if (!IsDisposed)
                    {
                        System.Diagnostics.Debug.WriteLine("disposing");
                        Dispose();
                    }
                    System.Diagnostics.Debug.WriteLine("after dispose");
                }
            }
        }

выдаёт:

Player xxx.mp4 disposed <<это из деструктора
before dispose
after dispose
Программа "[13936] Zeratool.exe" завершилась с кодом 0 (0x0).

Я не понимаю, как это может быть, если уже был вызван Close() и программа к этому моменту уже должна была закрыться :man_shrugging:

Видимо тут что-то такое и есть

Оно только отправляет WM_CLOSE
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Form.cs,3570

С Environment.Exit наверно сразу.

Ну это понятно. Обёртка над WinAPI же.
Тогда получается, что главный цикл не прекратится, пока весь код этого потока не выполнится? Чёт я никогда об этом не задумывался :thinking:
Если сделать

Close();
do
{
 Application.DoEvents();
}
while (true);

то программа никогда не закроется?

Видимо да, проверьте ) Наверно только если без Application.DoEvents(). Не потока же, а очереди сообщений.

Но если так сделать при открытом окне, то винда выдаст диалог о зависшем приложении.

Да-да