Подтвердить что ты не робот

Наконец, блок не работает?

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

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), появится окно с сообщением "XXX перестала работать". Если я сейчас нажму кнопку "Отмена", он запустит окончательно, И если я подожду, пока он не будет выглядеть, и нажмите кнопку "Закрыть программу", он также запустит окончательно.

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

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

EDIT: Я запускаю это из командной строки в режиме деблокирования (встроенный режим выпуска) в Windows 7 32 бит. Сообщение об ошибке: первый результат ниже работает на 3.5, закрывая окно после того, как окна ищут проблему, во-вторых, когда я запускаю его на 4.0 и делаю то же самое.

alt text

4b9b3361

Ответ 1

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

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

Я предполагаю, что они имеют другое поведение в отношении завершения процесса сбоя. WerFault.exe явно убивает процесс .NET немедленно, тогда как .NET Error Reporting Shim как-то закрывает приложение, так что блок 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 в журнале событий и не завершает процесс.

Ответ 2

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

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

Ответ 3

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

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

Решение проблемы (если вы абсолютно НЕ ИМЕЕСЬ, чтобы ваши блоки, наконец, выполнялись), было бы: a) помещено в глобальный обработчик ошибок или b) заставить .NET всегда запускать блоки блоков и делать то, что они делали ( возможно, неправильный путь) в .NET 1.1. Поместите следующее в свой app.config:

<legacyUnhandledExceptionPolicy enabled="1">

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

Причина, по которой работает app.config, заключается в том, что для .NET 1.0 и 1.1 у CLR был глобальный улов в ней, который проглатывал Исключения до того, как они пошли неуправляемым, что, будучи уловкой, конечно, запустит наконец-то блоки для запуска, Конечно, рамки не могут достаточно знать об упомянутом исключении, чтобы обрабатывать его, например, переполнение стека, поэтому, вероятно, это неправильный способ сделать это.

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

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

Ответ 4

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

Из .NET Framework 4 Проблемы с миграцией:

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

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

Ответ 5

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