Why does this program crash: passing of std::string between DLLs
This isn't actually being caused by differing heap implementations - the MSVC std::string implementation doesn't use dynamically allocated memory for strings that small (it uses the small string optimization). The CRTs do need to match, but that isn't what bit you this time.
What's happening is that you're invoking undefined behaviour by violating the One Definition Rule.
The release and debug builds will have different preprocessor flags set, and you'll find that std::string
has a different definition in each case. Ask your compiler what sizeof(std::string)
is - MSVC10 tells me that it's 32 in a debug build and 28 in a release build (this isn't padding - 28 and 32 are both 4 bytes` boundaries).
So what's happening? Variable s
is initialized using the debug version of the copy constructor to copy a release version of std::string
. The offsets of the member variables are different between the versions, so you copy garbage. The MSVC implementation effectively stores begin and end pointers - you've copied garbage into them; because they're no longer null, the destructor tries to free them and you get an access violation.
Even if the heap implementations were the same it would crash, as you're freeing garbage pointers to memory that was never allocated in the first place.
In summary: the CRT versions need to match but so do the definitions - including the definitions in the standard library.
Could it be that the string A::getString() returns is being allocated in A.dll and freed in main.exe?
Yes.
If so, why - and what would be a safe way to pass strings between DLLs (or executables, for that matter)? Without using wrappers like shared_ptr with a custom deleter.
Using a shared_ptr
sounds like a sensible thing to me. Remember, as a rule of thumb, allocations and deallocations should be done by the same module to avoid glitches like these.
Exporting STL objects across dlls is at best a tricky pony. I suggest you check out this MSDN KB article first and this post.
You need to link to the same runtime lib (the DLL one), either debug or release, for every DLL in your app where memory is allocated in one and freed in another. (The reason for using the dynamically linked runtime lib is that then there will be one heap for your entire process, as opposed to one per dll/exe that links to the static one.)
This includes returning std::string and stl-containers by value, as that is what you do.
The reasons are twofold (updated section):
- the classes have different layouts/sizes, so differently compiled code assumes data is in different places. Whoever created it first gets right, but the other one will cause a crash sooner or later.
- the msvc heap-implementations are different in each runtime-lib which means that if you try to free a pointer in the heap that didn't allocate it, it will go bonkers. (This happens if the layouts are similar, i.e. where you outlive the first case.)
So, get your runtime libs straight, or stop freeing/allocating in different dlls (i.e. stop passing stuff by value).