Поведение исключений в делегатах в C# 2, размещенных в MS Excel и COM

Доброе утро,

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

Недавно у нас были большие проблемы с исключениями внутри делегатов для надстройки Microsoft Excel, что приводило к жесткому сбою в среде выполнения MSVC. Удаление делегатов решило эту проблему, но теперь мне любопытно узнать кровавые подробности.

В качестве краткого примера основного кода:

Delegate del; // initialized elsewhere
try
{
    del.DynamicInvoke();
}
catch(Exception e)
{
    /* Parsing of exception to generate user-friendly message here */
}

Приведенная выше конструкция допускала централизованную форму обработки ошибок и с точки зрения чистого кода была чистой и лаконичной. Каждая общедоступная функция была объявлена ​​как делегат и выполнялась через приведенный выше фрагмент.

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

Добавьте MS Excel, и вы получите хард-краши. Пошаговое выполнение кода покажет, где возникает ошибка, но никакого раскручивания стека, по-видимому, не происходит, прежде чем все рухнет в большом огненном шаре разрушения.

Моя гипотеза состоит в том, что COM, на котором размещена среда выполнения .NET (и, следовательно, наш код), делает что-то отличное от обычного выполнения кода .NET. Это убивает конечную точку, и Excel не знает об этом, который, в свою очередь, пытается получить доступ к конечной точке через COM только для того, чтобы обнаружить, что она каким-то образом исчезла, и Excel в ответ вылетает.

Это происходит только с комбинацией делегатов Excel+COM+, но я, честно говоря, не знаю, что больше влияет на это поведение... есть мысли?


person user25519    schedule 17.10.2008    source источник


Ответы (4)


Я думаю, что вы можете обнаружить здесь, что вы не освобождаете COM-объекты MS Excel, которые вы неявно создаете, когда возникает исключение. По моему опыту, приложения MS Office очень чувствительны к тому, что их ресурсы не высвобождаются (хотя большая часть моего опыта связана с Outlook).

Я бы не стал пытаться обрабатывать исключения на основе COM таким образом, если это вообще возможно. Если вам нужно централизованное ведение журнала, посмотрите на Application.ThreadException (для приложения WinForms) и AppDomain.CurrentDomain.UnhandledException.

Внутри ваших функций вы можете вызвать Marshal.ReleaseComObject() в блоках finally {} ваших методов, чтобы убедиться, что Excel не будет отключен при возникновении ваших исключений.

person Dave Markle    schedule 19.10.2008

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

Лучше используйте try/catch везде, где вам нужно (т.е. вокруг кода, который может выдать ошибку), а затем вызовите общую функцию для отображения/регистрации проблемы.

Кроме того, если вы используете разные потоки для доступа к COM-объектам Excel, может произойти много странных вещей.

Для правильного ответа на ваш вопрос необходима дополнительная информация:

  1. Всегда ли это происходит?
  2. Если нет, когда вы видите проблему, это управляемое исключение или исключение, созданное из объекта Excel?
  3. Можете ли вы создать простой метод, который просто генерирует исключение (выбрасывает новое ApplicationException();), и смотрите, есть ли у вас проблема?

Ваше здоровье

person Sunny Milenov    schedule 17.10.2008

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

person Community    schedule 16.02.2009

Приносим извинения за поздний ответ - загруженная неделя!

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

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

Всегда ли это происходит?

Да, если это неожиданное исключение, и оно всегда отображается как исключение .NET, возникающее в коде C#/.NET.

создайте простой метод, который просто выдает исключение ( throw new ApplicationException(); ), и посмотрите, есть ли у вас проблема?

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

вы не выпускаете COM-объекты MS Excel, которые вы неявно создаете, когда возникает исключение

Это звучит правдоподобно. Слой VBA передает данные из Excel через COM и в .NET, поэтому он вполне может делать что-то странное с владением ресурсами.

Если вам нужно централизованное ведение журнала, посмотрите на Application.ThreadException (для приложения WinForms) и AppDomain.CurrentDomain.UnhandledException.

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

вы можете вызвать Marshal.ReleaseComObject()

Интересно, я не знал об этой функции. Мы в основном игнорировали любой явный COM-код и использовали самый минимум и надеемся (!), что .NET CLR сделает свое волшебство...

Сейчас я обошел это, удалив делегатов, и все кажется счастливым. Просто нужно помнить, что нужно быть осторожным (или полностью избегать) делегатов и исключений в будущем коде Office + VBA + COM + CLR :-)

person user25519    schedule 21.10.2008
comment
Судя по моему опыту работы с Outlook Interop API, надежда на то, что ваши COM-объекты будут своевременно очищены сборщиком мусора, может привести именно к такому типу неприятностей. - person Dave Markle; 23.10.2008