We have a dynamic library ("HelloWorld.dll") that is compiled with Microsoft Visual Studio 2010 from the following source code:
#include <string> extern "C" __declspec(dllexport) std::string hello_world() { return std::string("Hello, World!"); // or just: return "Hello, World!"; }
And we also have an executable file (LoadLibraryExample.exe) that dynamically loads this DLL using the LoadLibrary WINAPI function:
#include <iostream> #include <string> #include <Windows.h> typedef std::string (*HelloWorldFunc)(); int main(int argc, char* argv[]) { if (HMODULE library = LoadLibrary("HelloWorld.dll")) { if (HelloWorldFunc hello_world = (HelloWorldFunc)GetProcAddress(library, "hello_world")) std::cout << hello_world() << std::endl; else std::cout << "GetProcAddress failed!" << std::endl; FreeLibrary(library); } else std::cout << "LoadLibrary failed!" << std::endl; std::cin.get(); }
This works great when connected to a dynamic runtime library ( / MD or / MDd ).
The problem occurs when I link them (the library and the executable) to the debug version of the static runtime library ( / MTd ). It seems that the program is working ("Hello, World!" Is displayed in the console window), but then it is issued with the following output:
HEAP[LoadLibraryExample.exe]: Invalid address specified to RtlValidateHeap( 00680000, 00413F60 ) Windows has triggered a breakpoint in LoadLibraryExample.exe. This may be due to a corruption of the heap, which indicates a bug in LoadLibraryExample.exe or any of the DLLs it has loaded. This may also be due to the user pressing F12 while LoadLibraryExample.exe has focus. The output window may have more diagnostic information.
The problem does not magically appear with the version of the static runtime library (switch / MT ). My assumption is that the release version just does not see the error, but it still exists.
After a little research, I found this page on MSDN, which says the following:
Using a statically linked CRT implies that any status information stored by the C runtime library will be local to this CRT instance.
Since a DLL built by binding to a static CRT will have its own CRT state, it is not recommended to link statically to CRT in a DLL, unless the consequences are specifically defined and understood.
Thus, the library and the executable file have their own copies of CRT, which have their own states. The std :: string instance is built in the library (with some internal memory allocations created by the CRT library), and then returned to the executable file. The executable file displays it, and then calls its destructor (leading to the release of internal memory by executable CRT). As far as I understand, an error occurs here: the std :: string base memory is allocated by one CRT and tries to free itself from another.
The problem does not arise if we return a primitive type (int, char, float, etc.) or a pointer from a DLL, because in these cases memory is not allocated or freed from it. However, an attempt to delete the returned pointer in the executable leads to the same error (and does not delete the pointer, obviously leading to a memory leak).
So the question is: is it possible to get around this problem?
PS: I really do not want to have a dependency on MSVCR100.dll and force users of my application to install any redistributable packages.
PPS: the above code produces the following warning:
warning C4190: 'hello_world' has C-linkage specified, but returns UDT 'std::basic_string<_Elem,_Traits,_Ax>' which is incompatible with C
which can be solved by removing extern "C" from the library function declaration:
__declspec(dllexport) std::string hello_world()
and changing the call to GetProcAddress as follows:
GetProcAddress(library, "?hello_world@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ")
(the name of the function is drawn up by the C ++ compiler, the actual name can be restored using the dumpbin.exe utility). The warning then disappeared, but the problem remains.
PPPS: I see a possible solution for providing a pair of functions in the library for each such situation: one that returns a pointer to some data, and another that removes a pointer to this data. In this case, the memory is allocated and removed from the same CRT. But this solution seems very ugly and unpretentious, since we must always work with pointers, and in addition, the programmer should always remember to call a special library function to delete the pointer instead of simply using the delete keyword.