Почему Environment.Exit () больше не завершает программу?

134

Это то, что я обнаружил всего несколько дней назад, я получил подтверждение, что этот вопрос не ограничивается только моей машиной .

Самый простой способ воспроизвести его - запустить приложение Windows Forms, добавить кнопку и написать следующий код:

    private void button1_Click(object sender, EventArgs e) {
        MessageBox.Show("yada");
        Environment.Exit(1);         // Kaboom!
    }

Программа завершается ошибкой после выполнения оператора Exit (). В Windows Forms вы получаете «Ошибка создания дескриптора окна».

Включение неуправляемой отладки позволяет понять, что происходит. COM цикл модальный выполняет и позволяет сообщение WM_PAINT быть доставлено. Это смертельно для распоряжающейся формы.

Единственные факты, которые я собрал до сих пор:

  • Это не ограничивается только работой с отладчиком. Это также терпит неудачу без одного. Также довольно плохо, диалоговое окно сбоя WER появляется дважды .
  • Это не имеет ничего общего с разрядностью процесса. Слой wow64 довольно печально известен, но сборка AnyCPU происходит так же.
  • Это не имеет ничего общего с .NET-версией, 4.5 и 3.5 аварийно завершают работу одинаково.
  • Код выхода не имеет значения.
  • Вызов Thread.Sleep () перед вызовом Exit () не исправляет это.
  • Это происходит в 64-разрядной версии Windows 8, и Windows 7, похоже, не подвержена такому же влиянию.
  • Это должно быть относительно новое поведение, я не видел этого раньше. Я не вижу соответствующих обновлений, доставляемых через Центр обновления Windows , хотя история обновлений на моем компьютере больше не точна.
  • Это грубо нарушающее поведение. Вы могли бы написать такой код в обработчике событий для AppDomain.UnhandledException, и он вылетает так же.

Мне особенно интересно, что вы могли бы сделать, чтобы избежать этой аварии. В частности, сценарий AppDomain.UnhandledException ставит меня в тупик; Есть не так много способов завершить программу .NET. Обратите внимание, что вызов Application.Exit () или Form.Close () недопустим в обработчике событий для UnhandledException, поэтому они не являются обходными путями.


ОБНОВЛЕНИЕ: Mehrdad указал, что поток финализатора мог быть частью проблемы. Я думаю, что вижу это, а также вижу некоторые свидетельства для 2-секундного тайм-аута, что CLR дает потоку финализатора завершить выполнение.

Финализатор находится внутри NativeWindow.ForceExitMessageLoop (). Там есть функция Win32 IsWindow (), которая примерно соответствует расположению кода, смещение 0x3c при просмотре машинного кода в 32-битном режиме. Похоже, что IsWindow () блокируется. Однако я не могу получить хорошую трассировку стека для внутренних устройств, отладчик считает, что вызов P / Invoke только что вернулся. Это сложно объяснить. Если бы вы могли получить лучшую трассировку стека, я бы с удовольствием ее увидел. Моя:

System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.ForceExitMessageLoop() + 0x3c bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Finalize() + 0x16 bytes
[Native to Managed Transition]
kernel32.dll!@BaseThreadInitThunk@12()  + 0xe bytes
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

Ничего, кроме вызова ForceExitMessageLoop, неуправляемый отладчик включен.

Ганс Пассант
источник
2
Я только что попробовал это с .NET 4, 4 Client Profile, 3.5, 3.5 Client Profile, 3.0 и 2.0, и не получил ошибку ни на одном из них. 64-битная Windows 7 - моя ОС, использующая VS2010.
Стив
2
@ Стив This happens on the 64-bit version of Windows 8Ханс сказал так!
Parimal Raj
7
Я могу повторить это (Win 8, 64-бит), скопировать / вставить ваш код и подключить кнопку, и я получаю точные симптомы, описанные.
клавиатураP
3
Приложение консольного режима не может продемонстрировать эту проблему, ничто не может пойти не так, когда Exit () продолжает качать сообщения.
Ганс Пассант
3
Я сталкивался с подобным поведением Exit(0)немного раньше, когда работал 64-битный Win7, изменение ExitCodeне помогло, теперь я использую его Process.GetCurrentProcess().Kill()без проблем
Sriram Sakthivel

Ответы:

85

Я связался с Microsoft по поводу этой проблемы, и это, казалось, окупилось. По крайней мере, я хотел бы думать, что это сделал :). Хотя я не получил от них подтверждения решения, с группой Windows сложно связаться напрямую, и мне пришлось использовать посредника.

Обновление, доставленное через Центр обновления Windows, решило проблему. Заметная 2-секундная задержка перед сбоем больше не присутствует, что настоятельно рекомендует устранить тупик IsWindow (). И программа выключается чисто и надежно. Обновление установило исправления для Защитника Windows, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll и wintrust.dll.

Uxtheme.dll странная утка. Он реализует API тем Visual Styles и используется этой тестовой программой. Я не могу быть уверен, но мои деньги на это как источник проблемы. Копия в C: \ WINDOWS \ system32 имеет номер версии 6.2.9200.16660, созданный 14 августа 2013 года на моей машине.

Дело закрыто.

Ганс Пассант
источник
11
История обновлений Windows на моем компьютере больше не точна. Все, что я знаю, это то, что он был установлен 14 августа.
Ганс Пассант
51

Я не знаю, почему это не работает "больше" , но я думаю, что Environment.Exitвыполняет ожидающие финализаторы. Environment.FailFastне делает.

Возможно, (по какой-то причудливой причине) у вас есть странные ожидающие финализаторы, которые должны запускаться позже, вызывая это.

user541686
источник
2
Вы можете быть к чему-то. Финализатор занят выполнением NativeWindow.ForceExitMessageLoop (). Как ни странно, он не вложен ни в один вызов.
Ганс Пассант
@HansPassant: Я хотел бы повторить проблему, чтобы я мог разобраться в ней, но я не могу. Вызов NativeWindow.ForceExitMessageLoopзастрял в управляемом или неуправляемом коде? Это даже застряло, или это занято - ожидание или ожидание сообщения или чего-то еще?
user541686
Это, безусловно, указывает на основную проблему. Я думаю, что в основе проблемы лежит функция WinWi IsWindow (). Я думаю, что я также вижу 2-секундный таймаут в потоке финализатора, после которого все идет к черту. Отладчик не показывает, что он выполняет вызов IsWindow (), но я уже видел, как Windows разыгрывает трюки со стеком, выключая его при вводе критического кода внутри Windows.
Ганс Пассант
4
Я думаю, что метод Environment.FailFast () для данного случая необработанных исключений, вероятно, является лучшим методом для использования в любом случае. (Я не знал об этом - спасибо!) Однако есть много унаследованного кода, который будет использовать Environment.Exit (), который, к сожалению, будет неловко падать :(
Ян Йейтс
2
Вы определенно на что-то. В моем случае я запустил IHost, используя IHost.StartAsync, чтобы выполнить некоторое интеграционное тестирование, и все же после вызова (и, конечно, ожидания) IHost.StopAsync процесс все еще не завершился. Только после вызова IHost.Dispose, процесс завершается. Спасибо за совет
Malte R
6

Это не объясняет, почему это происходит, но я бы не стал вызывать Environment.Exitобработчик событий кнопки, как ваш пример - вместо этого закройте основную форму, как это предлагается в ответе Рене .

Что касается AppDomain.UnhandledExceptionобработчика, может быть, вы могли бы просто установить Environment.ExitCodeвместо вызова Environment.Exit.

Я не уверен, что вы пытаетесь достичь здесь. Почему вы хотите вернуть код выхода из приложения Windows Forms? Обычно коды выхода используются консольными приложениями.

Меня особенно интересует, что вы могли бы сделать, чтобы избежать этого сбоя. Для предотвращения появления диалогового окна WER необходим вызов Environment.Exit ().

Есть ли у вас попробовать / поймать в методе Main? Для приложений Windows Forms у меня всегда есть попытка / зацепка вокруг цикла сообщений, а также обработчиков необработанных исключений.

Джо
источник
Уверен, ты должен звонить Application.Exitвместо Environment.Exit.
user541686
7
Извините, это не обходной путь. Вызов Environment.Exit () необходим для предотвращения отображения диалога WER. Обратите внимание на «известный факт», код выхода не имеет значения.
Ганс Пассант
7
@Hans: ловит ли AppDomain.UnhandledException, чтобы попытаться избежать диалога WER в первую очередь? Я имею в виду, если есть необработанное исключение, диалог WER должен показывать, не так ли?
Гарри Джонстон
2

Я нашел ту же проблему в нашем приложении, мы решили ее с помощью следующей конструкции:

Environment.ExitCode=1;
Application.Exit();
Джесси
источник