Деструкторы не вызываются, когда собственное (C ++) исключение распространяется на компонент CLR - PullRequest
9 голосов
/ 23 марта 2010

У нас есть большая часть собственного кода C ++, скомпилированного в DLL.

Затем у нас есть пара dll, содержащих прокси-код C ++ / CLI для обертывания интерфейсов C ++.

Кроме того, у нас есть код C #, вызывающий оболочки C ++ / CLI.

Стандартные вещи, пока что.

Но у нас есть много случаев, когда собственные исключения C ++ могут распространяться в мире .Net, и мы полагаемся на способность .Net обернуть их в объекты System.Exception, и по большей части это работает нормально.

Однако мы обнаружили, что деструкторы объектов в области видимости в точке броска не вызываются при распространении исключения!

После некоторых исследований мы обнаружили, что это довольно известная проблема. Однако решения / обходные пути кажутся менее последовательными. Мы обнаружили, что если нативный код скомпилирован с / EHa вместо / EHsc, то проблема исчезнет (по крайней мере, в нашем тестовом примере это произошло). Однако мы бы предпочли использовать / EHsc, поскольку сами переводим исключения SEH в исключения C ++, и мы бы предпочли, чтобы компилятор имел больше возможностей для оптимизации.

Существуют ли другие обходные пути для этой проблемы - кроме переноса каждого вызова через нативно управляемую границу в (нативный) try-catch-throw (в дополнение к слою C ++ / CLI)?

Ответы [ 4 ]

3 голосов
/ 23 марта 2010

Я думаю, вы не делаете это правильно. Использование _set_se_translator () уже требует компиляции с / EHa. Со страницы библиотеки MSDN :

You must use /EHa when using _set_se_translator.

А если серьезно, вы сломаете управляемый код, когда будете его использовать. CLR опирается на исключения SEH для обнаружения различных ошибок. Он использует SetUnhandledExceptionFilter, чтобы перехватить их. В частности, NullReferenceException и OverflowException (x86) возникают таким образом. Когда вы внедрите свой собственный блок __try, вы предотвратите попадание этого исключения в CLR. Хотя вы «справитесь» с этим, у вас не будет никаких следов точной причины исключения. И управляемые блоки try / catch не могут его обнаружить.

Лечение эффективности / EHa (если это действительно проблема) - это 64-битный код. Его раскрутка стека на основе таблицы функций очень эффективна с нулевыми издержками для блока try.

3 голосов
/ 24 марта 2010

На странице MSDN для переключателя компилятора / E указано следующее поведение: -

http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx

Вот соответствующая цитата: -

Если вы используете / EHs, то ваш улов предложение не будет ловить асинхронный исключения. Кроме того, в Visual C ++ 2005, все объекты в области, когда генерируется асинхронное исключение не будет уничтожен, даже если обработано асинхронное исключение.

В основном / EHsc - это оптимистичный взгляд - он предполагает, что единственными исключениями являются настоящие исключения в стиле C ++, и будет соответственно оптимизирован. / EHa, с другой стороны, принимает пессимистический взгляд и предполагает, что любая строка кода может вызвать генерацию исключения.

Если вы можете гарантировать, что никогда не станете причиной нарушения прав доступа, ошибки на странице или другого SEH, используйте / EHsc. Но если вы пишете услугу и / или хотите предоставить «лучшее из возможного», тогда / EHa будет необходимо.

Я также согласен с мнением @ JaredPar о недопущении исключений за пределы модуля. То, что @nobugz говорит о том, как CLR обрабатывает исключения, может быть правдой, но я думаю, что есть разница между .Net-кодом, обращающимся напрямую к нативному коду с использованием P / Invoke, и обращением в C ++ / CLI-взаимодействие DLL. В первом случае CLR должен справиться с ситуацией от вашего имени, тогда как во втором случае вы контролируете ситуацию и можете выполнить соответствующий перевод.

3 голосов
/ 23 марта 2010

К сожалению, нет, я не верю, что есть хорошие обходные пути.Реализация исключений C ++ на платформах MS реализована с использованием исключений SEH (IIRC).CLR подключается к обработке SEH, чтобы перехватить собственные исключения и обработать их в исключения CLR.Поскольку он ловит их на уровне SEH, исключение выглядит как исключение SEH для C ++, и деструкторы запускаются или не запускаются соответствующим образом.

Итак, как вы уже заметили, лучшими двумя вариантами являются

  • Компиляция с / EHa
  • Добавить попытку / перехват в точке входа и выхода ваших функций

В идеале, вы все равно должны заниматься вторым.По моему опыту считается плохой практикой позволять исключениям C ++ пересекать границы компонентов.

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

1 голос
/ 20 октября 2011

В версии 2.0 CLR есть ошибка, вызывающая эту проблему. Запуск вашего управляемого исполняемого файла в CLR 4.0 позволит вызывать деструкторы, как и ожидалось.

Подробнее см. Повышение общего мьютекса после освобождения .

...