примеры - типы исключений c#




Почему исключение.NET не попало в блок try/catch? (17)

Я работаю над проектом, использующим библиотеку парсеров 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.


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

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

AppDomain.CurrentDomain.UnhandledException 

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

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


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

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

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

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


@spoulson,

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

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

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


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


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


Используете ли вы .Net 1.0 или 1.1? Если это так, catch (Exception ex) не будет перехватывать исключения из неуправляемого кода. Вместо этого вам нужно будет использовать catch {}. См. Эту статью для получения дополнительной информации:

http://www.netfxharmonics.com/2005/10/net-20-trycatch-and-trycatchexception/


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

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

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

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


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

Я согласен с Даниэлем в том, что, возможно, исключение происходит в отдельном потоке - попробуйте подключить событие исключения потока в Application.ThreadException. Это должно возникать при возникновении любого необработанного исключения. Вы можете адаптировать свой код таким образом: -

using System.Threading;

...

void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
  throw new ParserException(e.Exception.Message, e.Exception);
}    

 ...

 var exceptionHandler = 
    new ThreadExceptionEventHandler(Application_ThreadException);
 Application.ThreadException += exceptionHandler;
 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);
 }
 finally {
    Application.ThreadException -= exceptionHandler;
 }

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


Стив Штайнер прав, что исключение происходит в библиотеке antlr, проходя через метод mTokens () и попадая в библиотеку antlr. Проблема в том, что этот метод автоматически генерируется antlr. Поэтому любые изменения для обработки исключения в mTokens () будут перезаписаны, когда вы сгенерируете классы parser / lexer.

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

Добавьте этот код в свой грамматический (.g) файл. Вам также необходимо отключить «Включить только мой код» в меню отладки.

@members {

    public override Object RecoverFromMismatchedSet(IIntStream input,RecognitionException e,    BitSet follow)  
    {
        throw e;
    }
}

@rulecatch {
    catch (RecognitionException e) 
    {
        throw e;
    }
}

Это моя попытка версии C # примера, приведенного в главе «Выход из распознавателя при первой ошибке» в книге «Окончательный справочник ANTLR».

Надеюсь, это то, что вы искали.


Я загрузил ваш код, и все работает так, как ожидалось.

Отладчик Visual Studio правильно перехватывает все исключения. Блокировка блоков работает, как ожидалось.

Я запускаю сервер Windows 2003 SP2, VS2008 Team Suite (9.0.30729.1 SP)

Я попытался скомпилировать проект для .NET 2.0, 3.0 и 3.5

@Steve Steiner, варианты отладчика, о которых вы говорили, не имеют никакого отношения к этому поведению.

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


Я загрузил образец проекта VS2008, и здесь тоже немного тупик. Однако я смог преодолеть исключения, хотя, вероятно, не так, как будет работать, для вас это будет отлично. Но вот что я нашел:

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

Оттуда я добавил пару фиктивных классов в основной файл program.cs:

class MyNoViableAltException : Exception
{
    public MyNoViableAltException()
    {
    }
    public MyNoViableAltException(string grammarDecisionDescription, int decisionNumber, int stateNumber, Antlr.Runtime.IIntStream input)
    {
    }
}
class MyEarlyExitException : Exception
{
    public MyEarlyExitException()
    {
    }

    public MyEarlyExitException(int decisionNumber, Antlr.Runtime.IIntStream input)
    {
    }
}

а затем добавили используемые строки в TimeDefParser.cs и TimeDefLexer.cs:

using NoViableAltException = MyNoViableAltException;
using EarlyExitException = NoViableAltException; 

При этом исключения будут пузыриться в поддельных классах исключений и могут быть обработаны там, но в методе mTokens в TimeDefLexer.cs все еще есть исключение. Обнаружение исключения в том, что в попытке поймать в этом классе исключение:

            try
            {
                alt4 = dfa4.Predict(input);
            }
            catch
            {
            }

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


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

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

как раз то, что ожидается .


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

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

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

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


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

  1. Что говорит полное сообщение об ошибке? Что это за исключение?
  2. Основываясь на трассировке стека, которую вы указали здесь, не является ли исключение вашим кодом в TimeDefLexer.mTokens ()?

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

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

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


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





antlr