Returning Strings from DLL Functions

2019-02-09 02:25发布

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.

7条回答
淡お忘
2楼-- · 2019-02-09 03:05

Update:

I have compiled your sample in both VS2008 and VS2010 and I was able to successfully compile and execute without a problem. I compiled the library both as a static and a dynamic library.

Original:

The following relates to my discussion with bdk and imaginaryboy. I did not delete it as it might be of some interest to someone.

Ok this question really bothers me because it looks like its being passed by value not by reference. There does not appear to be any objects created in the heap it looks to be entirely stack based.

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

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;
}

The Disassembly:

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] 
}

The 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 it applies:

Even if the code is in a seperate DLL the string that is returned is copied by value into the callers stack. There is a hidden param that passes the object to GetGreetings(). I do not see any heap getting created. I do not 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!";
}
查看更多
登录 后发表回答