Returning Strings from DLL Functions
Solution 1
Since your error message indicates you're using Microsoft C++ I'll offer an MS specific answer.
As long as you compile both the EXE and the DLL with the SAME compiler, and both link the the SAME version of the runtime DYNAMICALLY then you'll be just fine. For example, using "Multi-threaded DLL" for both.
If you link against the runtime statically, or link against different versions of the runtime then you're SOL for the reasons @Billy ONeal points out (memory will be allocated in one heap and freed in another).
Solution 2
This occurs because you're allocating memory in one DLL (using std::string's constructor), and freeing it in another DLL. You can't do that because each DLL typically sets up it's own heap.
Solution 3
You may return a std::string
from a class as long as you inline the function that creates the string. This is done, for example, by the Qt toolkit in their QString::toStdString
method:
inline std::string QString::toStdString() const
{ const QByteArray asc = toAscii(); return std::string(asc.constData(), asc.length()); }
The inline function is compiled with the program that uses the dll, not when the dll is compiled. This avoids any problems encountered by incompatible runtimes or compiler switches.
So in fact, you should only return standard types from your dll (such as const char *
and int
above) and add inline functions to convert them to std::string()
.
Passing in a string &
parameter may also be unsafe because they are often implemented as copy on write.
Solution 4
Just like snmacdonald I was unable to repro your crash, under normal circumstances. Others already mentioned: Configuration Properties -> Code Generation -> Runtime Library must be exactly the same
If I change one of them I get your crash. I have both the DLL and EXE set to Multi-Threaded DLL.
Solution 5
Just like user295190 and Adam said that it would work fine if with the exactly same compilation settings.
For example, in Qt QString::toStdString() would return a std::string and you could use it in your EXE from QtCore.dll.
It would crash if DLL and EXE had different compiling settings and linking settings. For example, DLL linked to MD and EXE linked to MT CRT library.
Comments
-
Maxpm almost 2 years
For some reason, returning a string from a DLL function crashes my program on runtime with the error
Unhandled exception at 0x775dfbae in Cranberry Library Tester.exe: Microsoft C++ exception: std::out_of_range at memory location 0x001ef604..
.I have verified it's not a problem with the function itself by compiling the DLL code as an
.exe
and doing a few simple tests in themain
function.Functions with other return types (
int
,double
, etc.) work perfectly.- Why does this happen?
- Is there a way to work around this behavior?
Source code for DLL:
// Library.h #include <string> std::string GetGreeting();
.
// Library.cpp #include "Library.h" std::string GetGreeting() { return "Hello, world!"; }
Source code for tester:
// Tester.cpp #include <iostream> #include <Library.h> int main() { std::cout << GetGreeting() }
EDIT: I'm using VS2010.
Conclusion
A workaround is to make sure the library and source are compiled using the same compiler with the same options, etc.
-
Maxpm over 13 yearsDoes that mean I have to use 100%-"normal" types (i.e.,
char*
)? Is there really no way to do this? -
pkh over 13 years@maxpm: if this is the case, you might be able to pass a std::string in as an output argument; e.g.:
void GetGreeting(string& s) { s.assign("Hello World!"); }
This may or may not work, depending on how the allocators track between the DLL and not-DLL code. You'd want to try it putting a much larger string in, just to be sure. -
bdk over 13 yearsJust a thought, would using a custom allocator on the std::strings that lives completely on one side of the DLL boundary help? That could possibly ensure allocation and freeing of memory all occurs from the same heap
-
Admin over 13 yearsI dont understand why the heap would be used at all? It is passed by value and should be returned by value on the stack.
-
bdk over 13 yearsThe string is passed by value on the stack, so a new string object should be created on the stack by calling its copy constructor. That constructor may be utilizing heap storage for the actual text, if it stored that on the stack, resizing the string later would be problematic
-
Admin over 13 years@bdk: That clarifies it. Strange it works for a static lib in VS2008.
-
Maxpm over 13 yearsTo clarify, the library must be compiled as a DLL with the same settings? Will it not work if it's compiled as a static library? If it does work, do the settings still have to be the same?
-
Billy ONeal over 13 years@bdk: That should work -- of course then it wouldn't be a
std::string
anymore though. (It would be astd::basic_string<char, std::char_traits<char>, MyAllocatorType>
) -
klaus triendl over 10 yearsA bit late, but worth to add to Billy's answer: a custom allocator can capture the allocation functions
operator new
andoperator delete
of the current translation unit, thus 'binding' the allocator to the originating DLL. See mymodulebound_allocator
that I've successfully used for years. Instead ofstd::string
/std::wstring
I usedtypedef std::basic_string<char, std::char_traits<char>, modulebound_allocator<char[]> > mystring
throughout the project -
Billy ONeal over 10 years@Klaus: That won't work for the OP's case -- the same compiler / version need to be used.
-
klaus triendl over 10 years@Billy Hmm, the same applies to your answer because of C++ user-defined objects in general... but the
modulebound_allocator
can be used when linking to the static runtime, so I don't see the issue regarding my answer? -
Billy ONeal over 10 years@Klaus: On different compiler versions, the innards of
std::string
itself don't match. Trying to look at, for example, Visual C++ 2012'sstd::string
in GCC, or even in another version of Visual C++, wouldn't work, because the data layout is different. Your proposed fix solves the case where someone followed the really bad route of statically linking the CRT into a DLL. But if the compiler versions didn't match then bad things will still happen. -
klaus triendl over 10 years@Billy Agreed, never said something different. I just don't see the reasoning on targeting my comment, which just adds to your mentioned
MyAllocatorType
-
Billy ONeal over 10 years@Klaus: Only reason is that its recent :) The other comments were posted before the OP clarified that he was using different compilers / compiler versions.