9

I have the problem with passing by reference std::string to function in dll.

This is function call:

CAFC AFCArchive;

std::string sSSS = std::string("data\\gtasa.afc");

AFCER_PRINT_RET(AFCArchive.OpenArchive(sSSS.c_str()));
//AFCER_PRINT_RET(AFCArchive.OpenArchive(sSSS));
//AFCER_PRINT_RET(AFCArchive.OpenArchive("data\\gtasa.afc"));

This is function header:

#define AFCLIBDLL_API __declspec(dllimport) 
AFCLIBDLL_API EAFCErrors CAFC::OpenArchive(std::string const &_sFileName);

I try to debug pass-by-step through calling the function and look at _sFileName value inside function.

_sFileName in function sets any value(for example, t4gs..\n\t).

I try to detect any heap corruption, but compiler says, that there is no error.

DLL has been compiled in debug settings. .exe programm compiled in debug too.

What's wrong?? Help..!

P.S. I used Visual Studio 2013. WinApp.

EDIT

I have change header of func to this code:

AFCLIBDLL_API EAFCErrors CAFC::CreateArchive(char const *const _pArchiveName)
{
    std::string _sArchiveName(_pArchiveName);
    ...

I really don't know, how to fix this bug...

About heap: it is allocated in virtual memory of our process, right? In this case, shared virtual memory is common.

Aleksey
  • 140
  • 1
  • 8
  • Were the EXE and DLL compiled with exactly the same compiler, using exactly the same flags? Otherwise the definitions of `std::string` and other STL types might be different between the EXE and DLL, and that can cause issues. – cf- Mar 09 '14 at 06:14
  • I'd recommend that you keep your DLLs to using a well-defined ABI . Passing allocated memory across a DLL interface is asking for trouble. The DLL and the calling application might not be using the same heap, for example. – M.M Mar 09 '14 at 06:33
  • @MattMcNabb, why would that be a problem if they didn't use the same heap? They still share the same address space, right? – zneak Mar 09 '14 at 06:38
  • Well, if he adds to the string and it frees its memory and allocates new memory, the other heap will fail to free because it doesn't know about that block, etc. – M.M Mar 09 '14 at 06:42
  • `basic_string(const _Elem *_Ptr)` - it was called after .c_str(). `_Ptr` at the moment of entering in function is right: "data\\gtasa.afc" – Aleksey Mar 09 '14 at 07:49

3 Answers3

10

The issue has little to do with STL, and everything to do with passing objects across application boundaries.

1) The DLL and the EXE must be compiled with the same project settings. You must do this so that the struct alignment and packing are the same, the members and member functions do not have different behavior, and even more subtle, the low-level implementation of a reference and reference parameters is exactly the same.

2) The DLL and the EXE must use the same runtime heap. To do this, you must use the DLL version of the runtime library.

You would have encountered the same problem if you created a class that does similar things (in terms of memory management) as std::string.

Probably the reason for the memory corruption is that the object in question (std::string in this case) allocates and manages dynamically allocated memory. If the application uses one heap, and the DLL uses another heap, how is that going to work if you instantiated the std::string in say, the DLL, but the application is resizing the string (meaning a memory allocation could occur)?

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
5

C++ classes like std::string can be used across module boundaries, but doing so places significant constraints on the modules. Simply put, both modules must use the same instance of the runtime.

So, for instance, if you compile one module with VS2013, then you must do so for the other module. What's more, you must link to the dynamic runtime rather than statically linking the runtime. The latter results in distinct runtime instances in each module.

And it looks like you are exporting member functions. That also requires a common shared runtime. And you should use __declspec(dllexport) on the entire class rather than individual members.

If you control both modules, then it is easy enough to meet these requirements. If you wish to let other parties produce one or other of the modules, then you are imposing a significant constraint on those other parties. If that is a problem, then consider using more portable interop. For example, instead of std::string use const char*.


Now, it's possible that you are already using a single shared instance of the dynamic runtime. In which case the error will be more prosaic. Perhaps the calling conventions do not match. Given the sparse level of detail in your question, it's hard to say anything with certainty.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • It's precisely for this reason that COM defines a narrow set of (fairly inconvenient) types and rules about object ownership. The OP might we well advised to research the conventions as it provides a robust solution to this problem. Also worth pointing out that traditionally release and debug builds are incompatible when using STL on Windows (this is because of additional sanity checking when building for debug, resulting in objects being different sized and layed out). – marko Mar 09 '14 at 12:19
  • @Marko That's covered by "use the sane instance of the RTL". COM would be a decent choice here. Binary interop. – David Heffernan Mar 09 '14 at 12:25
1

I encountered similar problem. I resolved it synchronizing Configuration Properties -> C / C++ settings.

If you want debug mode:

  • Set _DEBUG definition in Preprocessor Definitions in both projects.
  • Set /MDd in Code Generation -> Runtime Library in both projects.

If you want release mode:

  • Remove _DEBUG definition in Preprocessor Definitions in both projects.
  • Set /MD in Code Generation -> Runtime Library in both projects.

Both projects I mean exe and dll project.
It works for me especially if I don't want to change any settings of dll but only adjust to them.

wathson
  • 21
  • 3