Returning C++ objects from Windows DLL - c++

Due to how Microsoft implements the heap in their non-DLL versions of the runtime, returning a C++ object from a DLL can cause problems:
// dll.h
DLL_EXPORT std::string somefunc();
and:
// app.c - not part of DLL but in the main executable
void doit()
{
std::string str(somefunc());
}
The above code runs fine provided both the DLL and the EXE are built with the Multi-threaded DLL runtime library.
But if the DLL and EXE are built without the DLL runtime library (either the single or multi-threaded versions), the code above fails (with a debug runtime, the code aborts immediately due to the assertion _CrtIsValidHeapPointer(pUserData) failing; with a non-debug runtime the heap gets corrupted and the program eventually fails elsewhere).
Two questions:
Is there a way to solve this other then requiring that all code use the DLL runtime?
For people who distribute their libraries to third parties, how do you handle this? Do you not use C++ objects in your API? Do you require users of your library to use the DLL runtime? Something else?

Is there a way to solve this other then requiring that all code use the DLL runtime?
Not that I know of.
For people who distribute their libraries to third parties, how do you handle this? Do you not use C++ objects in your API? Do you require users of your library to use the DLL runtime? Something else?
In the past I distributed an SDK w/ dlls but it was COM based. With COM all the marshalling of parameters and IPC is done for you automatically. Users can also integrate in with any language that way.

Your code has two potential problems: you addressed the first one - CRT runtime. You have another problem here: the std::string could change among VC++ versions. In fact, it did change in the past.
The safe way to deal with is to export only C basic types. And exports both create and release functions from the DLL. Instead of export a std::string, export a pointer.
__declspec(export) void* createObject()
{
std::string* p = __impl_createObject();
return (void*)p;
}
__declspec(export) void releasePSTRING(void* pObj)
{
delete ((std::string*)(pObj));
}

There is a way to deal with this, but it's somewhat non-trivial. Like most of the rest of the library, std::string doesn't allocate memory directly with new -- instead, it uses an allocator (std::allocator<char>, by default).
You can provide your own allocator that uses your own heap allocation routines that are common to the DLL and the executable, such as by using HeapAlloc to obtain memory, and suballocate blocks from there.

If you have a DLL that you want to distribute and you don't want to bind your callers to a specific version to the C-Runtime, do any of the following:
I. Link the DLL to the static version of the C-Runtime library. From the Visual Studio Project Properties page, select the tab for Configuration Properties-> C/C++ -> Code Generation. These an option for selecting the "Runtime library". Select "Multithread" or "Multithreaded Debug" instead of the DLL versions. (Command line equilvalent is /MT or /MTd)
There are a couple of different drawbacks to this approach:
a. If Microsoft ever releases a security patch of the CRT, your shipped components may be vulnerable until your recompile and redist your binary.
b. Heap pointers allocated by "malloc" or "new" in the DLL can not be "free"d or "delete"d by the EXE or other binary. You'll crash otherwise. The same holds true for FILE handles created by fopen. You can't call fopen in teh DLL and expect the EXE to be able to fclose on it. Again, crash if you do. You will need to build the interface to your DLL to be resilient to all of these issues. For starters, a function that returns an instance to std::string is likely going to be an issue. Provide functions exported by your DLL to handle the releasing of resources as needed.
Other options:
II. Ship with no c-runtime depedency. This is a bit harder. You first have to remove all calls to the CRT out of your code, provide some stub functions to get the DLL to link, and specify "no default libraries" linking option. Can be done.
III. C++ classes can be exported cleanly from a DLL by using COM interface pointers. You'll still need to address the issues in 1a above, but ATL classes are a great way to get the overhead of COM out of the way.

The simple fact here is, Microsofts implementation aside, C++ is NOT an ABI. You cannot export C++ objects, on any platform, from a dynamic module, and expect them to work with a different compiler or language.
Exporting c++ classes from Dlls is a largely pointless excercise - because of the name mangling, and lack of support in c++ for dynamically loaded classes - the dlls have to be loaded statically - so you loose the biggest benefit of splitting a project into dlls - the ability to only load functionality as needed.

Related

C++ | Windows - Passing STL objects to DLL

I have seen some very well questions but I decided I would ask my own because they weren't quite what I wanted to know.
There was a lot of talk about how you shouldn't pass std::string into a function (from a DLL) because everything has to match CRT, Platform version, etc but can you safely pass a const *char to a function and then inside of the .dll use std::string's?
The reason I ask is because I recently discovered how amazingly useful C++ strings are and I want to use them with my .DLL but I have discovered they are unsafe to use with .dll's because of runtime templates and such.
So my question is mainly, is there a "safe" way to use std::string in a .dll?
It's entirely safe to use it internally.
And don't worry too much about using a std::string across DLL boundaries either. You can't use it in a public interface, but when you ship a DLL with your own program you control both sides. It's quite common to have both the DLL and the EXE in two Visual Studio projects within one VS solution. That ensures they're built with the same platform version etc.
Feel free to use std::string in DLL interface. But you should make sure that DLL and Application must be built with the same std library to avoid std::string boundary problem.
There is no ABI documented, so technically, it can change with the compiler version, and can depend on compiler settings.
You should be on the safe side if caller and DLL are built by the same version of Visual C++.
However this means: you cannot use it in a public interface, and you are giving up compatibility between versions (say, running an oder EXE with a newer DLL - after upgrading your compiler).

Debug DLL with release EXE

Is it possible to execute debug mode DLL with release mode EXE?
I am trying this scenario but EXE does not load the debug DLL and throws error "This application has failed to start...".
I know this is not good scenario to do but Due to certain requirements I have to make this work.
It can work if your dll interface has no dependencies on classes that may look different in debug and release.
e.g. std::string and std::vector in MSVC are not compatible in debug and release. (Fences ...)
So for example
std::string GetName();
will not work.
In additional new and delete should not be shifted because debug/release use different runtimes. But anyway you should always delete in the same context(dll/exe) as new.
Yes, this can work.
Your "application has failed to start" issue is most likely you copied a debug build of the DLL (built on your machine with Visual Studio), to a machine that did not have the DEBUG CRT installed. Usually copying over MSVCRTD(version).dll to the same directory as your program files solves this problem. I have a previous answer that covers some of this here.
Best bet is to always have all your binaries linked to the same dynamic MSVCRT DLL so they all share the same runtime.
Another easy workaround is to compile your DEBUG DLL to use the same flavor of the MSVCRT DLL (or statically link to the CRT). Somewhere in the VS project propery pages (code generation I think) is the dropdown for choosing the CRT. There's nothing wrong with linking the retail MSVCRT into a debug DLL - or statically linking.
The things to watch out for are when you have a different flavor of the debug C runtime linked to the different binaries. If you have the release MSVCRT dll linked for the EXE, but the debug MSCVRTD DLL for the DLL, that can cause problems in a few circumstances. This is because handles and memory blocks are tracked by two different instances of the CRT.
Examples:
If you allocate memory in the EXE, but free in in the DLL. And vice versa.
File handles opened with fopen() in the EXE, but used or closed in the EXE (and vice versa).
For any of the header files for the DLL's interface, having any sort of inline functions or methods implemented in the header file is an easy way to cause #1 or #2 to happen.
Sharing STL objects (std::string, std::list, std::vector) is a definite no-no for mixed CRT usage.

Potential memory risks from linking a statically built library to a shared library using Visual Studio

I'm working on a cross-platform Firebreath plugin which is crashing on Windows. I use a static library containing classes which reference boost.asio. When I link this library against the plugin dll, I observe crashes when interacting with the io_service subsystem (ie, during socket construction). When I link the static library against an ordinary executable, the problem does not occur. When I compile the contents of the static library directly into the plugin dll project, the crash does not occur. I have gone to great lengths to ensure that all aspects of my build environment on Windows are consistent (build modes, version of Visual Studio, etc). In addition, I've firewalled the boost.asio headers, so that the plugin dll code has no visibility of the boost.asio sub-system (to no effect, unfortunately, vs2008 and vs2010). As far as I can tell, I've done everything possible to ensure that the build environment is well behaved but the problem persists.
Can the community offer any advice on potential risks or approaches which could expose or solve the problem?
Two things that are dramatically different between linking a static library vs loading a DLL:
global initialization: In a DLL, they all run. In a static library, the linker only brings in an object file if it satisfies some unresolved external, so systems which rely on components registering themselves using the constructor or intiialization expression of a global object fail.
shared CRT: In a static library, all calls to the Standard library are resolved during link time, and the main application and library function all call the same copy of the Standard library. In a DLL, you risk having two copies of the Standard library, which can be ok if you're careful never to share any Standard library objects (like FILE*, std::string, or even malloc/free pairs) between library and application.
The second thing is most likely what's biting you. There's a lazy way to fix it: Use the Standard library DLL, and a better way of fixing it: figure out memory and object lifetime and don't try to allocate on one side and free on the other, or share C++ class layout across the boundary. Virtual functions work fine across the boundary.
The advantage of the "better" way is that plugins can be built in any version of the compiler, which becomes a big deal for maintenance later on in the development cycle.
In the FireBreath plugin prep script, try turning on the WITH_DYNAMIC_MSVC_RUNTIME flag.

Writing a DLL that loads msvcr80.dll and exposes the free()-function

I have a third-party DLL that depends on MSVCR80 and allocates resources that I need to cleanup. The library does not expose a free-function for doing this. Instead, I need to load the same runtime library and manually call the free function.
As a workaround I'm trying to write a "wrapper" DLL that loads the correct runtime and exposes the free function. This DLL is created using Visual Studio 2010 and is dependent on a separate runtime library. Doing LoadLibrary("msvcr80.dll") fails with error R6034 which I guess is because of manifest issues.
Is it even possible to load msvcr80.dll using LoadLibrary? Do I need to create a manifest, embed it into the DLL and store msvcr80.dll in the same directory as my wrapper DLL?
I realize that this is a flaw in the third-party library, but I'm pretty much stuck with this version. Getting the vendor to fix this is most likely not an option.
Probably there are better solutions, but in case everything else failed you could find somewhere a copy of VC++ 2005 Express Edition (=free, no piracy is needed ;) ), which uses the version 8.0 of the compiler, and thus the same runtime of the defective dll.
Then you would build your wrapper dll with it, which would just call the free provided by its CRT (double check that you're using the dll version!).

Freeing memory allocated in a different DLL

I have an EXE file using a DLL file which is using another DLL file. This situation has arisen:
In DLL file 1:
class abc
{
static bool FindSubFolders(const std::string & sFolderToCheck,
std::vector< std::string > & vecSubFoldersFound);
}
In DLL file 2:
void aFunction()
{
std::vector<std::string> folders;
std::string sLocation;
...
abc::FindSubFolders(sLocation, folders)
}
In release mode, everything works fine. But in debug mode, I come up with an assertion failure in the destructor of one of the std::strings in the folders vector (when folders goes out of scope at the end of aFunction):
dbgheap.c : line 1274
/*
* If this ASSERT fails, a bad pointer has been passed in. It may be
* totally bogus, or it may have been allocated from another heap.
* The pointer MUST come from the 'local' heap.
*/
_ASSERTE(_CrtIsValidHeapPointer(pUserData));
I assume this is because the memory has been allocated on DLL file 1's heap, but is being freed in DLL file 2.
The comment in dbgheap.c seems pretty insistent that this is a problem.
Why is this such a problem, when it seems to work fine if I just ignore it? Is there a non-assertion-failing way of doing this?
As sean has already said, the release build simply ignores that delete statement, so the best you can hope for is a memory leak.
If you have control over how both DLL files are compiled, make sure to use the Multi-threaded Debug DLL (/MDd) or Multi-threaded DLL (/MD) settings for the runtime library. That way, both DLL files will use the same runtime system and share the same heap.
The downside is that you need to install the runtime system together with your application (Microsoft offers an installer for that). It will work fine on your development machine since Visual Studio installs that runtime system too, but on a freshly installed machine it will report missing DLL files.
Most likely, the release build has the same problem, but release builds don't assert. They just ignore the problem. You might never see an issue. Or you might see data corruption. Or you might see a crash. Maybe only your users will experience bugs that you are simply not able to reproduce.
Don't ignore CRT assertions.
You should always use the appropriate deallocator (the one that matches the allocator used to begin with). If you are using static CRT libraries in your DLL files, the DLL files are using different heaps. You can not deallocate memory across heaps. Allocate and deallocate a block of memory using the same heap.
If you are using shared CRT libraries in your DLL files, then they should be using the same heap and you can allocate in one DLL file and deallocate in another.
As other says, the problem can be solved by making sure that the CRT is shared between the two modules. But there are common scenarios where this contract is hard to enforce.
The reason is that making sure to link against a shared CRT will not work if the EXE and DLL do not link against the same CRT version (as in 6.0, 7.0, 8.0). For example if you take a DLL that has been built in VC6.0 and try to use it with an EXE build in VS2010 you will get the same issue as before. The two versions of CRT will be loaded in your process and will each use their own heap for allocation, regardless if your EXE and DLL use 'shared' CRT, they will not be the same.
A better practice for an API would be to make sure that objects allocated in one side are also destroyed on the same side. I know this sounds ugly but it is the only way to ensure that a DLL remains binary compatible.
This is only a problem if the application or one (or more) of the DLL files is linked against the static version of the standard library. This was solved about a decade ago by MS releasing the shared library version of the standard library. This is because each version of the standard library will build it own internal heap and thus with multiple heaps you must release the memory to the correct heap. By using the shared version of the standard library they all use the same heap.
It is standard practice nowadays for the application and all DLL files should be built to use the dynamic version of the standard library.
The only caveat to the above is if you create your own heap and allocate memory from this heap. But this is a very specialized procedure only done in rare situations (and if you understand enough to use it then you will not be in the situation of asking this question).