Чому 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 Update , хоча історія оновлень більше не є точною на моїй машині.
  • Це грубо порушує поведінку. Ви б написали подібний код у обробнику подій для AppDomain.UnhandledException, і він виходить з ладу таким же чином.

Мене особливо цікавить, що ви могли зробити, щоб уникнути цієї аварії. Особливо сценарій AppDomain.UnhandledException мене натикає; Існує не так багато способів припинити програму .NET. Зауважте, що виклик Application.Exit () або Form.Close () не дійсний в обробці подій для UnhandledException, тому вони не є обхідними способами.


ОНОВЛЕННЯ: Мехрдад зазначив, що нитка фіналізатора може бути частиною проблеми. Я думаю, що я бачу це, а також бачу деякі докази за 2 секунди часу, що CLR дає нитку фіналізатора для завершення виконання.

Фіналізатор знаходиться всередині NativeWindow.ForceExitMessageLoop (). Там функція IsWindow () Win32, яка приблизно відповідає розташуванню коду, зміщення 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
@Steve 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 Update, вирішило проблему. Помітна затримка на 2 секунди перед збоєм більше не присутня, що настійно говорить про те, що IsWindow () тупик вирішено. І програма вимикається чисто і надійно. В оновлення встановлені виправлення для Windows Defender, 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не робить.

Можливо, у вас є (дивним чином, химерна причина) дивні фіналізатори, що очікують на розгляд, які повинні запуститися після цього, і це призведе до цього.


2
Ви можете бути на чомусь. Фіналізатор зайнятий виконанням NativeWindow.ForceExitMessageLoop (). Як не дивно, він не вкладається в жодному дзвінку.
Ганс Пасант

@HansPassant: Я б хотів, щоб я міг спростувати проблему, щоб я міг її розглянути, але не можу. Чи дзвінок NativeWindow.ForceExitMessageLoopзастряг у керованому чи некерованому коді? Це навіть застрягло, чи це зайнято в очікуванні чи очікуванні повідомлення чи чогось іншого?
користувач541686

Це, звичайно, вказує на основну проблему. Я думаю, що саме в основі проблеми лежить виннапі функція IsWindow (). Я думаю, що я також бачу 2 секунди тайм-аут на фіналізаційній нитці, після якого все йде в пекло. Налагоджувач не показує, що він виконує виклик IsWindow (), але я бачив, як Windows відтворює трюки зі стеком раніше, вимикаючи його під час введення критичного коду всередині Windows.
Ганс Пасант

4
Я думаю, що метод Environment.FailFast (), для даного випадку необроблених винятків, мабуть, найкращий спосіб використовувати в будь-якому випадку. (Я не знав про це - дякую!) Однак є багато застарілого коду, який би використовував Environment.Exit (), який, на жаль, аварійно зазнає аварії :(
Ian Yates

2
Ви, безумовно, до чогось. У моєму випадку я запустив IHost, використовуючи IHost.StartAsync, щоб провести тестування інтеграції, але після виклику (і, звичайно, очікування) IHost.StopAsync процес все ще не закінчився. Лише після виклику IHost.Dispose процес припиняється. Дякую за пораду
Malte R

6

Це не пояснює, чому це відбувається, але я б не закликав Environment.Exitобробник подій кнопки, як ваш зразок - замість цього закрити основну форму, як це запропоновано у відповіді Рене .

Що стосується AppDomain.UnhandledExceptionобробника, можливо, ви могли просто встановити, Environment.ExitCodeа не телефонувати Environment.Exit.

Я не впевнений, чого ви намагаєтеся досягти тут. Чому ви хочете повернути вихідний код із програми Windows Forms? Зазвичай коди виходу використовуються консольними програмами.

Мене особливо цікавить, що ви могли б зробити, щоб уникнути цього аварійного виклику. Екзит () необхідний для запобігання показу діалогового вікна WER.

Чи маєте ви пробувати / уловлювати метод Main? У додатках Windows Forms у мене завжди є спроба / піймання навколо циклу повідомлень, а також не оброблені обробники винятків.


Досить впевнений, що ви повинні дзвонити Application.Exitзамість Environment.Exit.
користувач541686

7
Вибачте, це не обхід. Виклик Environment.Exit () необхідний для запобігання показу діалогового вікна WER. Зверніть увагу і на "відомий факт", вихідний код не має значення.
Ганс Пасант

7
@Hans: ловить AppDomain.UnhandledException, щоб спробувати уникнути, перш за все, законного діалогу WER? Я маю на увазі, якщо є необроблений виняток, діалогове вікно WER повинно відображатися, чи не так?
Гаррі Джонстон

2

Я знайшов таку ж проблему в нашому додатку, ми вирішили її за допомогою наступної конструкції:

Environment.ExitCode=1;
Application.Exit();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.