In this case, the problem is that I was passing a std::string
back across a .dll boundary.
Runtime Library config
If the MSVC Runtime library
is set to Multi-threaded Debug DLL (/MDd)
, then this is no problem (it works fine).
If the MSVC Runtime library
is set to Multi-threaded Debug (/MTd)
, then it will throw this error, which can be fixed with the following instructions.
Memory allocated in Memory Manager A and freed in Memory Manager B ...
The problem is that memory is allocated on the .dll side, then that same memory is freed on the application side. This means that memory manager A is allocating memory, and memory manager B is releasing that same memory, which generates errors.
The solution is to make sure that all memory passed back is not allocated in the DLL. In other words, the memory is always allocated on the application side, and freed on the application side.
Of course, the DLL can allocate/free memory internally - but it can't allocate memory that is later freed by the application.
Examples
This will not work:
// Memory is allocated on the .dll side, and freed on the app side, which throws error.
DLL std::string GetString();
This will work:
// Memory is allocated/freed on the application side, and never allocated in the .dll.
DLL int GetString(std::string& text);
However, this is not quite enough.
On the application side, the string has to be pre-allocated:
std::string text("");
text.reserve(1024); // Reserves 1024 bytes in the string "text".
On the .dll side, the text must be copied into the original buffer (rather than overwritten with memory that is allocated on the .dll side):
text.assign("hello");
Sometimes, C++ will insist on allocating memory anyway. Double check that the pre-allocation is still the same as it was:
if (text.capacity < 1024)
{
cout << "Memory was allocated on the .dll side. This will eventually throw an error.";
}
Another way that works is to use std::shared_ptr<std::string>
, so even though memory is allocated in the .dll, it is released by the .dll (rather than the application side).
Yet another way is to accept a char *
and a length which indicates the amount of pre-allocated memory. If the text that we want to pass back is longer than the length of pre-allocated memory, return an error.