Question

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.

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top