Returning Strings from DLL Functions

14,059

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.

Share:
14,059
Maxpm
Author by

Maxpm

/* No comment. */

Updated on June 04, 2022

Comments

  • Maxpm
    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 the main 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
    Maxpm over 13 years
    Does that mean I have to use 100%-"normal" types (i.e., char*)? Is there really no way to do this?
  • pkh
    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
    bdk over 13 years
    Just 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
    Admin over 13 years
    I 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
    bdk over 13 years
    The 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
    Admin over 13 years
    @bdk: That clarifies it. Strange it works for a static lib in VS2008.
  • Maxpm
    Maxpm over 13 years
    To 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
    Billy ONeal over 13 years
    @bdk: That should work -- of course then it wouldn't be a std::string anymore though. (It would be a std::basic_string<char, std::char_traits<char>, MyAllocatorType>)
  • klaus triendl
    klaus triendl over 10 years
    A bit late, but worth to add to Billy's answer: a custom allocator can capture the allocation functions operator new and operator delete of the current translation unit, thus 'binding' the allocator to the originating DLL. See my modulebound_allocator that I've successfully used for years. Instead of std::string/std::wstring I used typedef std::basic_string<char, std::char_traits<char>, modulebound_allocator<char[]> > mystring throughout the project
  • Billy ONeal
    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
    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
    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's std::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
    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
    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.