Destructors not executed (no stack unwinding) when exception is thrown
Asked Answered
B

1

1

I found a very very weird behaviour that I have never seen before. I'm working on a complex VS2005 C++ project.

class Tester
{
public:
    Tester()
    {
        TRACE("Construct Tester");
    }
    ~Tester()
    {
        TRACE("~Destruct Tester");
    }
};

void Thrower()
{
    Tester X;
    throw std::exception("Booom");
}

What do you expect to see in Trace output when Thrower() is called? That Tester is constructed and then destructed when the stack is unwinded, or not?

At least I expect that, but the destructor of Tester is never called!

Impossible !?!?!?!

Is this a bug in Visual Studio ?

I searched a lot but not even on Stackoverflow I found an answer.

Benzedrine answered 25/1, 2014 at 3:56 Comment(0)
B
1

It took me an entire day to find out what was wrong.

Now I have to explain a little deeper what I'm doing. I have a C++ code that compiles into a LIB file. The code above (Tester and Thrower) sits in this plain C++ LIB file.

And I have another C++ code that compiles into a Managed C++ DLL which links with this LIB file. So at the end both codes are in the same DLL. I have managed wrapper functions that call the code in the LIB file like this:

ManagedWrapper()
{
    try
    {
        Thrower();
    }
    catch (std::exception& e)
    {
        throw new System::Exception(e.what());
    }
}

This does NOT work. With this code I have memory leaks and network sockets that are not closed. The stack in Thrower is not unwound.

The reason for this is that stack unwinding does not take place before catch is reached. But when catch sits in another library than throw the stack is not unwound. The DLL does not know how to unwind the stack in the LIB file (although both are finally compiled into the same DLL!!)

But I found an extremely simple solution.

In the LIB file I had to add an intermediate function between the ManagedWrapper() and the Thrower() like this. The code looks stupid, but it solves the problem:

Catcher()
{
    try
    {
         Thrower();
    }
    catch(...) // unwind HERE
    {
        throw;
    }
}

The important thing is that this catcher must sit in the LIB file where the exception is thrown. When the exception is catched, the stack is unwound and then the exception is re-thrown to the managed wrapper.

Sometimes code that looks stupid is very intelligent!

SUMMARY: Never forget that exception must always be catched in the same library where they have been thrown. If they are catched across a library boundary you have severe problems.

Benzedrine answered 25/1, 2014 at 3:56 Comment(3)
I'd like to hear from a language lawyer as to whether the C++ standard allows this, I suspect what you are describing is a bug in the implementation.Millennium
If it was a bug in Visual Studio there should be any information in internet. But I could not find anything. But maybe you are right.Benzedrine
try the code in latest version of VS, it appears that this has already been fixed, this msdn link shows why it was a bug: social.msdn.microsoft.com/Forums/vstudio/en-US/…Hooge

© 2022 - 2024 — McMap. All rights reserved.