Returning lines from DLL functions

For some reason, returning a string from a DLL function causes my program to crash at 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 did not check, not a problem with the function itself, compiling the DLL code as .exe and doing some simple tests in the main function.

Functions with other return types ( int , double , etc.) work fine.

  • Why is this happening?
  • Is there any way around this behavior?

Download source package for dll:

 // Library.h #include <string> std::string GetGreeting(); 

.

 // Library.cpp #include "Library.h" std::string GetGreeting() { return "Hello, world!"; } 

Source code of the tester:

 // Tester.cpp #include <iostream> #include <Library.h> int main() { std::cout << GetGreeting() } 

EDIT: I am using VS2010.


Conclusion

The workaround is to make sure that the library and source are compiled using the same compiler with the same parameters , etc.

+9
source share
7 answers

Since your error message indicates that you are using Microsoft C ++, I offer a specific MS answer.

As long as you compile both the EXE and the DLL along with the SAME compiler, and both are linked to the SAME version of the DYNAMICALLY runtime, then you will be fine. For example, using a "multi-threaded DLL" for both.

If you refer to static at run time or refer to different versions of the runtime, you are SOL for the reasons indicated by @Billy ONeal (memory will be allocated on one heap and freed on another).

+5
source

This is because you are allocating memory in one DLL (using the std :: string constructor) and freeing it in another DLL. You cannot do this because each DLL usually sets up its own heap.

+6
source

Update:

I collected your sample in VS2008 and VS2010, and I was able to successfully compile and execute without problems. I compiled a library of both a static and a dynamic library.

Original:

The following applies to my discussion with bdk and imaginaryboy. I did not delete it, as this may be of interest to someone.

Well, this question really bothers me because it looks like it is passed by value not by reference. It seems that there are no objects created on the heap, they look completely stack based.

I did a quick test to check how objects are transferred to Visual Studios (compiled in release mode without time optimization and link time optimization).

The code:

 class Foo { int i, j; public: Foo() {} Foo(int i, int j) : i(i), j(j) { } }; Foo builder(int i, int j) { Foo built(i, j); return built; } int main() { int i = sizeof(Foo); int j = sizeof(int); Foo buildMe; buildMe = builder(i, j); //std::string test = GetGreeting(); //std::cout << test; return 0; } 

Dismantling:

 int main() { 00AD1030 push ebp 00AD1031 mov ebp,esp 00AD1033 sub esp,18h int i = sizeof(Foo); 00AD1036 mov dword ptr [i],8 int j = sizeof(int); 00AD103D mov dword ptr [j],4 Foo buildMe; buildMe = builder(i, j); 00AD1044 mov eax,dword ptr [j] ;param j 00AD1047 push eax 00AD1048 mov ecx,dword ptr [i] ;param i 00AD104B push ecx 00AD104C lea edx,[ebp-18h] ;buildMe 00AD104F push edx 00AD1050 call builder (0AD1000h) 00AD1055 add esp,0Ch 00AD1058 mov ecx,dword ptr [eax] ;builder i 00AD105A mov edx,dword ptr [eax+4] ;builder j 00AD105D mov dword ptr [buildMe],ecx 00AD1060 mov dword ptr [ebp-8],edx return 0; 00AD1063 xor eax,eax } 00AD1065 mov esp,ebp 00AD1067 pop ebp 00AD1068 ret Foo builder(int i, int j) { 01041000 push ebp 01041001 mov ebp,esp 01041003 sub esp,8 Foo built(i, j); 01041006 mov eax,dword ptr [i] 01041009 mov dword ptr [built],eax ;ebp-8 built i 0104100C mov ecx,dword ptr [j] 0104100F mov dword ptr [ebp-4],ecx ;ebp-4 built j return built; 01041012 mov edx,dword ptr [ebp+8] ;buildMe 01041015 mov eax,dword ptr [built] 01041018 mov dword ptr [edx],eax ;buildMe (i) 0104101A mov ecx,dword ptr [ebp-4] 0104101D mov dword ptr [edx+4],ecx ;buildMe (j) 01041020 mov eax,dword ptr [ebp+8] } 

Stack:

 0x003DF964 08 00 00 00 .... 0x003DF968 04 00 00 00 .... 0x003DF96C 98 f9 3d 00 ˜ù=. 0x003DF970 55 10 ad 00 U.. 0x003DF974 80 f9 3d 00 €ù=. 0x003DF978 08 00 00 00 .... ;builder i param 0x003DF97C 04 00 00 00 .... ;builder j param 0x003DF980 08 00 00 00 .... ;builder return j 0x003DF984 04 00 00 00 .... ;builder return i 0x003DF988 04 00 00 00 .... ;j 0x003DF98C 08 00 00 00 .... ;buildMe i param 0x003DF990 04 00 00 00 .... ;buildMe j param 0x003DF994 08 00 00 00 .... ;i 0x003DF998 dc f9 3d 00 Üù=. ;esp 

Why is it used:

Even if the code is in a separate DLL, the returned string is copied by value to the stack of callers . There is a hidden parameter that passes the object to GetGreetings() . I do not see the creation of the heap. I don't see the heap having anything to do with the problem.

 int main() { 01021020 push ebp 01021021 mov ebp,esp 01021023 push 0FFFFFFFFh 01021025 push offset __ehhandler$_main (10218A9h) 0102102A mov eax,dword ptr fs:[00000000h] 01021030 push eax 01021031 sub esp,24h 01021034 mov eax,dword ptr [___security_cookie (1023004h)] 01021039 xor eax,ebp 0102103B mov dword ptr [ebp-10h],eax 0102103E push eax 0102103F lea eax,[ebp-0Ch] 01021042 mov dword ptr fs:[00000000h],eax std::string test = GetGreeting(); 01021048 lea eax,[ebp-2Ch] ;mov test string to eax 0102104B push eax 0102104C call GetGreeting (1021000h) 01021051 add esp,4 01021054 mov dword ptr [ebp-4],0 std::cout << test; 0102105B lea ecx,[ebp-2Ch] 0102105E push ecx 0102105F mov edx,dword ptr [__imp_std::cout (1022038h)] 01021065 push edx 01021066 call dword ptr [__imp_std::operator<<<char,std::char_traits<char>,std::allocator<char> > (102203Ch)] 0102106C add esp,8 return 0; 0102106F mov dword ptr [ebp-30h],0 01021076 mov dword ptr [ebp-4],0FFFFFFFFh 0102107D lea ecx,[ebp-2Ch] 01021080 call dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (1022044h)] 01021086 mov eax,dword ptr [ebp-30h] } std::string GetGreeting() { 01021000 push ecx ;ret + 4 01021001 push esi ;ret + 4 + 4 = 0C 01021002 mov esi,dword ptr [esp+0Ch] ; this is test string std::string greet("Hello, world!"); 01021006 push offset string "Hello, world!" (1022124h) 0102100B mov ecx,esi 0102100D mov dword ptr [esp+8],0 01021015 call dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (1022040h)] return greet; 0102101B mov eax,esi ;put back test 0102101D pop esi //return "Hello, world!"; } 
+1
source

I had a problem that was resolved:

  • using the runtime library as a DLL for the application and DLL (so that the selection is a descriptor in a unique place)

  • make sure that all project parameters (compiler, linker) are the same for both projects

+1
source

Like snmacdonald, I could not reproduce your accident under normal circumstances. Others already mentioned: Configuration Properties -> Code Generation -> Runtime Library should be exactly the same

If I change one of them, I get your failure. I have both DLL and EXE installed for a multi-threaded DLL.

+1
source

You can return std::string from the class while you inline the function that creates the string. This is done, for example, using the Qt toolkit in the QString::toStdString :

 inline std::string QString::toStdString() const { const QByteArray asc = toAscii(); return std::string(asc.constData(), asc.length()); } 

The built-in function is compiled using a program that uses dll, not when compiling a dll. This avoids any problems arising from incompatible operating modes or compilers.

Thus, you should only return standard types from your DLL (for example, const char * and int above) and add inline functions to convert them to std::string() .

Passing in the string & parameter may also be unsafe, as they are often implemented as a copy on write.

+1
source

Like user295190, Adam said that it will work fine if it has the exact same compilation settings.

For example, in Qt, QString :: toStdString () will return std :: string, and you can use it in your EXE from QtCore.dll.

This will work if the DLL and EXE have different compilation and link settings. For example, a DLL associated with MD and EXE is associated with the MT CRT library.

+1
source

All Articles