Я обнаружил очень и очень странное поведение, которого никогда не видел прежде.
Я работаю над сложным проектом VS2005 C ++.
class Tester
{
public:
Tester()
{
TRACE("Construct Tester");
}
~Tester()
{
TRACE("~Destruct Tester");
}
};
void Thrower()
{
Tester X;
throw std::exception("Booom");
}
Что вы ожидаете увидеть в выводе трассировки, когда Thrower()
называется?
Этот тестер создается, а затем разрушается, когда стек не разматывается, или нет?
По крайней мере, я ожидаю этого, но деструктор Tester никогда не называется!
Невозможно !?!?!?!
Это ошибка в Visual Studio?
Я много искал, но даже не в Stackoverflow я нашел ответ.
Мне потребовался целый день, чтобы выяснить, в чем дело.
Теперь я должен немного глубже объяснить, что я делаю.
У меня есть код C ++, который компилируется в файл LIB. Код выше (Tester and Thrower) находится в этом простом файле C ++ LIB.
И у меня есть другой код C ++, который компилируется в управляемую DLL C ++, которая связана с этим файлом LIB. Таким образом, в конце оба кода находятся в одной и той же DLL. Я управлял функциями-обертками, которые вызывают код в файле LIB следующим образом:
ManagedWrapper()
{
try
{
Thrower();
}
catch (std::exception& e)
{
throw new System::Exception(e.what());
}
}
Это делает НЕ Работа.
С этим кодом у меня есть утечки памяти и сетевые сокеты, которые не закрыты.
Стек в Thrower не разматывается.
Причина этого заключается в том, что разматывание стека не происходит до того, как будет достигнут улов. Но когда catch сидит в другой библиотеке, чем throw, стек не разматывается. DLL не знает, как развернуть стек в файле LIB (хотя оба они, наконец, скомпилированы в одну и ту же DLL !!)
Но я нашел чрезвычайно простое решение.
В файле LIB мне нужно было добавить промежуточную функцию между ManagedWrapper () и Thrower () следующим образом. Код выглядит глупо, но он решает проблему:
Catcher()
{
try
{
Thrower();
}
catch(...) // unwind HERE
{
throw;
}
}
Важно то, что этот перехватчик должен находиться в LIB-файле, где выдается исключение. Когда исключение перехвачено, стек разматывается, а затем исключение повторно отправляется в управляемую оболочку.
Иногда код, который выглядит глупо, очень умный!
РЕЗЮМЕНикогда не забывайте, что исключение всегда должно быть перехвачено в той же библиотеке, в которую оно было брошено. Если они попадают через границы библиотеки, у вас серьезные проблемы.
Других решений пока нет …