Почему исключение .NET не перехватывается блоком try / catch? - PullRequest
42 голосов
/ 30 августа 2008

Я работаю над проектом с использованием библиотеки синтаксического анализатора ANTLR для C #. Я построил грамматику для разбора текста, и она работает хорошо. Однако, когда синтаксический анализатор обнаруживает недопустимый или неожиданный токен, он выдает одно из многих исключений. Проблема в том, что в некоторых случаях (не во всех) мой блок try / catch не будет его перехватывать и вместо этого останавливает выполнение как необработанное исключение.

Для меня проблема в том, что я не могу повторить эту проблему нигде, кроме как в своем полном коде. Стек вызовов показывает, что исключение определенно происходит в моем блоке try / catch (Exception). Единственное, о чем я могу думать, - это то, что между моим кодом и кодом, генерирующим исключение, происходит несколько вызовов сборки ANTLR, и в этой библиотеке не включена отладка, поэтому я не могу пройти через нее. Интересно, не-отлаживаемые сборки запрещают всплытие исключений? Стек вызовов выглядит следующим образом; вызовы внешних сборок в Antlr.Runtime:

    Expl.Itinerary.dll!TimeDefLexer.mTokens() Line 1213 C#
    Antlr3.Runtime.dll!Antlr.Runtime.Lexer.NextToken() + 0xfc bytes 
    Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.FillBuffer() + 0x22c bytes   
    Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.LT(int k = 1) + 0x68 bytes
    Expl.Itinerary.dll!TimeDefParser.prog() Line 109 + 0x17 bytes   C#
    Expl.Itinerary.dll!Expl.Itinerary.TDLParser.Parse(string Text = "", Expl.Itinerary.IItinerary Itinerary = {Expl.Itinerary.MemoryItinerary}) Line 17 + 0xa bytes C#

Фрагмент кода из самого нижнего вызова в Parse () выглядит следующим образом:

     try {
        // Execution stopped at parser.prog()
        TimeDefParser.prog_return prog_ret = parser.prog();
        return prog_ret == null ? null : prog_ret.value;
     }
     catch (Exception ex) {
        throw new ParserException(ex.Message, ex);
     }

Для меня предложение catch (Exception) должно было охватить любое исключение. Есть ли причина, по которой это не так?

Обновление: Я проследил через внешнюю сборку с помощью Reflector и не обнаружил никаких свидетельств о потоке. Похоже, что сборка является всего лишь классом служебной программы для сгенерированного кода ANTLR. Исключение выдается из метода TimeDefLexer.mTokens (), а его тип - NoViableAltException, который происходит от RecognitionException -> Exception. Это исключение выдается, когда лексер не может понять следующий токен в потоке; другими словами, неверный ввод. Это исключение ДОЛЖНО происходить, однако оно должно было быть перехвачено моим блоком try / catch.

Кроме того, повторное выбрасывание ParserException действительно не имеет отношения к этой ситуации. Это уровень абстракции, который принимает любое исключение во время синтаксического анализа и преобразует его в мое собственное исключение ParserException. Проблема обработки исключений, с которой я столкнулся, никогда не достигает этой строки кода. Фактически, я прокомментировал часть «throw new ParserException» и все еще получил тот же результат.

Еще одна вещь, я изменил исходный рассматриваемый блок try / catch, чтобы вместо этого перехватывать NoViableAltException, устраняя любую путаницу наследования. Я все еще получил тот же результат.

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

Чувак, я все еще в тупике! Я не упоминал об этом раньше, но я использую VS 2008, и весь мой код 3.5. Внешняя сборка 2.0. Кроме того, некоторые из моего кода являются подклассами класса в сборке 2.0. Может ли несовпадение версий вызвать эту проблему?

Обновление 2: Мне удалось устранить конфликт версий .NET, портировав соответствующие части моего кода .NET 3.5 в проект .NET 2.0 и повторив тот же сценарий. Мне удалось реплицировать одно и то же необработанное исключение при постоянной работе в .NET 2.0.

Я узнал, что ANTLR недавно выпустил 3.1. Итак, я обновился с 3.0.1 и повторил попытку. Оказывается, сгенерированный код немного переработан, но в моих тестовых примерах происходит то же необработанное исключение.

Обновление 3: Я повторил этот сценарий в упрощенном проекте VS 2008 . Не стесняйтесь загружать и проверять проект для себя. Я применил все замечательные предложения, но пока не смог преодолеть это препятствие.

Если вы можете найти обходной путь, пожалуйста, поделитесь своими выводами. Еще раз спасибо!


Спасибо, но VS 2008 автоматически разбивается на необработанные исключения. Кроме того, у меня нет диалога Debug-> Exceptions. Создаваемое исключение NoViableAltException предназначено для перехвата кода пользователя. Поскольку он не перехвачен должным образом, выполнение программы неожиданно останавливается как необработанное исключение.

Исключение выдается из Exception, и многопоточность с ANTLR не выполняется.

Ответы [ 25 ]

0 голосов
/ 30 августа 2008

"Кроме того, вы можете поместить некоторый код в поймать все необработанные исключения. Читать ссылка для получения дополнительной информации, но основы эти две строки. "

Это неверно. Раньше он перехватывал все необработанные исключения в .NET 1.0 / 1.1, но это была ошибка, и не предполагалось, и это было исправлено в .NET 2.0.

AppDomain.CurrentDomain.UnhandledException 

Предназначен только для использования в качестве последнего шанса для регистрации, поэтому вы можете записать исключение до выхода из программы. Это не будет ловить исключение начиная с версии 2.0 (хотя, по крайней мере, в .NET 2.0 есть значение конфигурации, которое вы можете изменить, чтобы оно работало как 1.1, но не рекомендуется использовать это).

Стоит отметить, что есть несколько исключений, которые вы не можете отловить, например StackOverflowException и OutOfMemoryException. В противном случае, как полагают другие, это может быть где-то исключение в фоновом потоке. Также я почти уверен, что вы не можете перехватить некоторые / все неуправляемые / нативные исключения.

0 голосов
/ 30 августа 2008

Я не понимаю ... ваш блок catch просто генерирует новое исключение (с тем же сообщением). Это означает, что ваше утверждение:

Проблема в том, что в некоторых случаях (не во всех) мой блок try / catch не может его перехватить и вместо этого останавливает выполнение как необработанное исключение.

- это именно то, что ожидается .

0 голосов
/ 30 августа 2008

Я согласен с Daniel Auger и kronoz , что это пахнет как исключение, связанное с потоками. Кроме того, вот мои другие вопросы:

  1. Что говорит полное сообщение об ошибке? Что это за исключение?
  2. На основании предоставленной вами трассировки стека, не является ли исключение, выданное вашим кодом, в TimeDefLexer.mTokens ()?
0 голосов
/ 17 февраля 2017

Если вы используете com-объекты своего проекта и пытаетесь ловить блоки, чтобы не перехватывать исключения, вам понадобится отключить Инструменты / Отладка / Разрыв, когда исключения пересекают AppDomain или управляемые / собственные границы (только управляемые).

0 голосов
/ 02 сентября 2008

@ spoulson

Если вы можете повторить это, можете ли вы опубликовать это где-нибудь? Одним из способов, которым вы можете воспользоваться, является использование WinDBG с расширениями SOS для запуска приложения и перехвата необработанного исключения. При первом случайном исключении оно прервется (до того, как среда выполнения попытается найти обработчик), и вы сможете увидеть, откуда он поступает и какой поток.

Если вы раньше не использовали WinDBG, это может быть немного сложным, но вот хороший урок:

http://blogs.msdn.com/johan/archive/2007/11/13/getting-started-with-windbg-part-i.aspx

Как только вы запустите WinDBG, вы можете переключать режим обработки необработанных исключений, перейдя в Debug-> Event Filters.

...