Почему исключение .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 ]

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

Лично меня вообще не убеждает теория потоков.

Однажды, когда я видел это раньше, я работал с библиотекой, которая также определяла Исключение и те случаи, когда я имел в виду, что фактический Catch ссылался на другой тип «Исключение» (если он был полностью квалифицирован было Company.Lib.Exception, но это было не из-за использования), поэтому, когда дело доходит до перехвата нормального исключения, которое выдается (какое-то исключение аргумента, если я правильно помню), оно просто не перехватит его, потому что тип t match.

Итак, подведем итоги, есть ли другой тип исключения в другом пространстве имен, который используется в этом классе?

РЕДАКТИРОВАТЬ: быстрый способ проверить это - убедиться, что в своем предложении catch вы полностью квалифицировали тип Exception как «System.Exception» и добавили его!

РЕДАКТИРОВАТЬ2: ОК, я попробовал код и пока допускаю поражение. Я должен буду еще раз взглянуть на это утром, если никто не придумал решение.

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

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

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

мой блок try / catch не перехватит его и остановит выполнение как необработанное исключение.

Вам нужно найти причину процесса выхода. Это может быть что-то иное, чем необработанное исключение. Вы можете попробовать использовать собственный отладчик с точкой останова, установленной в «{,, kernel32.dll} ExitProcess». Затем используйте SOS , чтобы определить, какой управляемый код вызывает процесс выхода.

1 голос
/ 03 сентября 2008

Я считаю, что Стив Штейнер прав. Изучая предложения Стива, я наткнулся на эту ветку , в которой говорилось о параметре «Включить только мой код» в «Инструменты | Параметры | Отладчик | Общие». Предполагается, что отладчик будет зависать при определенных условиях, когда не пользовательский код генерирует или обрабатывает исключение. Я не совсем уверен, почему это вообще имеет значение, или почему отладчик говорит, что исключение было необработанным, когда оно действительно было.

Мне удалось устранить ложные разрывы, отключив параметр «Включить только мой код». Это также изменяет диалоговое окно «Отладка | Исключения», удаляя столбец «Обработано пользователем», так как он больше не применяется. Или вы можете просто снять флажок «Обработано пользователем» для CLR и получить тот же результат.

Большое спасибо всем за помощь!

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

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

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

Application.ThreadException += new ThreadExceptionEventHandler(ThreadExceptionHandler);

 // Catch all unhandled exceptions in all threads.
 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
1 голос
/ 30 августа 2008

Да, и в отношении того, что сказал Кибби; если вы выберете «Отладка | Исключения в VS» и просто нажмете все поля в «брошенном» столбце, он должен выбрать все до AFAIK как «исключение первого шанса», то есть VS покажет, когда исключение около будет обработано всем остальным и взломать соответствующий код. Это должно помочь с отладкой.

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

Вы пытались напечатать (Console.WriteLine ()) исключение внутри предложения catch, и не используете Visual Studio и не запускаете свое приложение на консоли?

1 голос
/ 01 сентября 2008

Я проследил через внешнюю сборку с помощью Reflector и не обнаружил никаких признаков резьбы.

Вы не можете найти ни одного потока, это не значит, что нет никакого потока

.NET имеет «пул потоков», который представляет собой набор «запасных» потоков, которые в основном бездействуют. Некоторые методы заставляют вещи выполняться в одном из потоков пула потоков, поэтому они не блокируют ваше основное приложение.

Яркими примерами являются такие вещи, как ThreadPool.QueueUserWorkItem , но есть множество других вещей, которые также могут запускать вещи в пуле потоков, которые не выглядят такими очевидными, как .BeginInvoke

На самом деле, вам нужно сделать то, что предлагает Кибби .

1 голос
/ 31 августа 2008

Лучший вариант звучит как установка Visual Studio для разрыва всех необработанных исключений (диалоговое окно «Отладка -> Исключения», установите флажок «Общие исключения времени выполнения языка» и, возможно, другие). Затем запустите вашу программу в режиме отладки. Когда код синтаксического анализатора ANTLR генерирует исключение, он должен быть перехвачен Visual Studio и позволить вам увидеть, где он возникает, тип исключения и т. Д.

Основываясь на описании, блок catch представляется правильным, поэтому может происходить одно из нескольких:

  1. парсер на самом деле не выдает исключение
  2. парсер в конечном итоге выдает что-то, что не является производным от System.Exception
  3. есть исключение, которое генерируется в другом потоке, который не обрабатывается

Похоже, вы потенциально исключили проблему № 3.

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

Я не уверен, что я неясен, но если это так, я вижу, как отладчик останавливает выполнение с «Необработанным исключением» типа NoViableAltException. Изначально я ничего не знал об этом пункте меню Debug-> Exceptions, потому что MS ожидает, что вы во время установки VS подтвердите профиль, если вы не знаете, чем они отличаются. Видимо, Я не был в профиле C # dev и пропустил эту опцию . После окончательной отладки всех сгенерированных исключений CLR я, к сожалению, не смог обнаружить какое-либо новое поведение, приводящее к причине этой необработанной проблемы исключения. Все сгенерированные исключения ожидались и предположительно обрабатывались в блоке try / catch.

Я просмотрел внешнюю сборку, и нет признаков многопоточности. Под этим я подразумеваю, что ссылки на System.Threading не существует, и делегаты не использовались вообще. Я знаком с тем, что представляет собой создание потока. Я проверяю это, наблюдая за набором инструментов Threads во время необработанного исключения, чтобы увидеть, что существует только один работающий поток.

У меня есть открытая проблема с людьми из ANTLR, поэтому, возможно, они уже могли решить эту проблему раньше. Мне удалось воспроизвести его в простом проекте консольного приложения с использованием .NET 2.0 и 3.5 под VS 2008 и VS 2005.

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

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

Ничего себе, так что из отчетов до сих пор 2 работали правильно, и 1 испытал проблему, о которой я сообщил. Какие версии Windows, Visual Studio используются и .NET Framework с номерами сборки?

Я использую XP SP2, VS 2008 Team Suite (9.0.30729.1 SP), C # 2008 (91899-270-92311015-60837) и .NET 3.5 SP1.

...