Наконец, блок не работает? - PullRequest
18 голосов
/ 16 ноября 2010

Хорошо, это довольно странная проблема, и я надеюсь, что кто-то может пролить свет. У меня есть следующий код:

static void Main(string[] args)
{
    try
    {
        Console.WriteLine("in try");
        throw new EncoderFallbackException();
    }
    catch (Exception)
    {
        Console.WriteLine("in Catch");
        throw new AbandonedMutexException();
    }
    finally
    {
        Console.WriteLine("in Finally");
        Console.ReadLine();
    }
}

СЕЙЧАС, когда я скомпилирую это для цели 3.5 (2.0 CLR), появится окно с надписью «ХХХ перестал работать». Если я теперь нажму на кнопку Отмена , она запустит команду finally, И если я подожду, пока она не завершит поиск, и нажму кнопку Закрыть программу , она также запустит команду finally.

Теперь, что интересно и сбивает с толку, так это ЕСЛИ я делаю то же самое, что скомпилировано против 4.0. Нажатие на кнопку Отмена запустит блок finally, а нажатие на кнопку Закрыть программу,

Мой вопрос: почему, наконец, запускается на 2.0, а не на 4.0 при нажатии кнопки Закрыть программу ? Каковы последствия этого?

РЕДАКТИРОВАТЬ: я запускаю это из командной строки в режиме выпуска (встроенный в режиме выпуска) на Windows 7 32 бит. Сообщение об ошибке: первый результат, показанный ниже, работает при закрытии 3.5 после того, как Windows ищет проблему, второй - когда я запускаю его на 4.0 и делаю то же самое.

alt text

Ответы [ 5 ]

20 голосов
/ 16 ноября 2010

Теперь я могу воспроизвести поведение (я не получил точных шагов из вашего вопроса, когда читал его в первый раз).

Я могу наблюдать одно отличие в том, что.NET Runtime обрабатывает необработанное исключение.CLR 2.0 запускает вспомогательный файл под названием Microsoft .NET Reporting Shim (dw20.exe), тогда как CLR 4.0 запускает Windows Error Reporting (WerFault.exe).

Я предполагаю, что оба имеют разное поведение в отношении завершения процесса сбоя.WerFault.exe, очевидно, немедленно убивает процесс .NET, тогда как Shim, сообщающий об ошибках .NET, каким-то образом закрывает приложение, так что блок finally все еще выполняется.

Также посмотрите на Просмотр событий: WerFault регистрирует ошибку приложения.уведомление о том, что аварийный процесс был прерван:

Application: ConsoleApplication1.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Threading.AbandonedMutexException
Stack:
   at Program.Main(System.String[])

dw20.exe, однако регистрирует только элемент информации с идентификатором события 1001 в журнале событий и не завершает процесс.

10 голосов
/ 16 ноября 2010

Подумайте, насколько ужасна эта ситуация: произошло нечто неожиданное, что никто никогда не писал код для обработки. Правильно ли делать в этой ситуации, чтобы запускал еще больше кода , который, вероятно, также не был создан для этой ситуации? Возможно нет. Часто правильная вещь здесь - это , а не попытка запустить блоки finally, потому что это сделает плохую ситуацию еще хуже. Вы уже знаете, что процесс идет вниз; немедленно избавь его от страданий.

В сценарии, где необработанное исключение приведет к остановке процесса, может произойти все, что угодно . Это определяется реализацией того, что происходит в этом случае: сообщается ли об ошибке в отчет об ошибках Windows, запускается ли отладчик и так далее. CLR полностью в пределах своих прав пытается запускать блоки finally, а также в пределах своих прав на быстрый отказ. В этом сценарии все ставки отключены; разные реализации могут делать разные вещи.

6 голосов
/ 16 ноября 2010

Все мои знания по этому вопросу взяты из этой статьи здесь: http://msdn.microsoft.com/en-us/magazine/cc793966.aspx - обратите внимание, что она написана для .NET 2.0, но у меня есть ощущение, что имеет смысл то, что мы испытывали в этом случае (подробнее чем "потому что решил" в любом случае)

Быстрый "У меня нет времени, чтобы прочитать эту статью" ответ (хотя вы должны, это действительно хороший):

Решение проблемы (если вам абсолютно НЕОБХОДИМО запустить ваши блоки finally) состоит в том, чтобы: а) включить глобальный обработчик ошибок или б) заставить .NET всегда запускать блоки окончательно и делать все так, как это делалось ( возможно, неправильный путь) в .NET 1.1 - поместите в свой app.config следующее:

<legacyUnhandledExceptionPolicy enabled="1">

Причина этого: Когда исключение выдается в .NET, оно начинает проходить назад по стеку в поисках обработчиков исключений, а когда оно находит его, оно выполняет вторую прогулку по стеку, выполняя блоки finally с до выполнения содержимого перехвата , Если он не находит перехвата, этот второй обход никогда не происходит, поэтому блоки finally здесь никогда не запускаются, поэтому глобальный обработчик исключений всегда будет запускать предложения finally, так как CLR будет запускать их при обнаружении перехвата, а НЕ при запуске (что, как я верю, означает, что даже если вы сделаете улов / бросок, ваши блоки, в конце концов, все равно будут запущены).

Причина, по которой исправление app.config работает, заключается в том, что для .NET 1.0 и 1.1 в CLR был глобальный улов, который проглатывал бы исключения, прежде чем они стали неуправляемыми, что, конечно, было бы хитом, вызывая запуск блоков finally , Конечно, фреймворк не может знать достаточно об указанном Exception, чтобы справиться с ним, например, переполнение стека, так что это, вероятно, неправильный способ сделать это.

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

Если вы находитесь в .NET 2.0+ без устаревшей обработки исключений, то ваше Исключение выпадет в систему обработки исключений Windows (SEH), которая кажется чертовски похожей на CLR, в которой она проходит через кадры до он не может найти перехват, а затем вызывает серию событий, называемых фильтром необработанных исключений (UEF). Это событие, на которое вы можете подписаться, но на него одновременно может быть подписана только ОДНА вещь, поэтому, когда что-то подписывается, Windows передает ему адрес обратного вызова, который был там раньше, что позволяет вам установить цепочку UEF обработчики - НО ОНИ НЕ ДОЛЖНЫ ХОЛОДИТЬ этот адрес, они должны сами назвать этот адрес, но если кто-то разорвет цепочку, бэп, вы больше не будете обрабатывать ошибки. Я предполагаю, что это то, что происходит, когда вы отменяете отчеты об ошибках Windows, это разрывает цепочку UEF, что означает, что приложение закрывается немедленно, а блоки finally не запускаются, однако, если вы позволите ему работать до конца и закроете его, это вызовет следующий UEF в цепочке. .NET зарегистрирует одно, из которого вызывается исключение AppDomain.UnhandledException (таким образом, даже это событие не гарантировано), которое, как я полагаю, также является местом, откуда вы вызываете свои блоки finally, поскольку я не могу понять, как, если вы никогда не переходите обратно в CLR может выполняться управляемый блок finally (статья не затрагивает этот бит.)

2 голосов
/ 16 ноября 2010

Я полагаю, что это как-то связано с изменениями в том, как подключен отладчик.

Из .NET Framework 4 Проблемы миграции документ:

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

Что происходит, если вы решите запустить отладчик, но вы отменили его.Я считаю, что это подпадает под эту категорию, и приложение просто останавливается из-за этого.

0 голосов
/ 16 ноября 2010

Запустил это и в выпуске, и в отладке, как в фреймворке 3.5, так и в 4.0, во всех случаях я вижу «в конечном итоге», да, запуск его из командной строки, дошел до закрытия моих сессий, возможно, это что-то на вашей машинеили, как указал Коби, может быть, это связано с платформой (я нахожусь на Win7 x64)

...