Properly linked DLLs and EXEs are supposed to have one freestore from which they all can allocate heap-based objects. Here is Chis Becke’s Answer in Who allocates heap to a DLL?:
… it is the C++ runtime that is responsible for creating its freestore and deciding how to allocate it. Specifically, if you use the Dll runtime option, then a single dll - msvcrtxx.dll - manages a single freestore that is shared between all dll's, and the exe, that are linked against that dll
Since this is true, then I should be able to new
objects in DLL/EXEs defined in other DLL/EXEs. According to Chris, the msvcrtxx.dll
and compile-time/runtime linker take care of where the joint freestore for all the DLL/EXEs can be obtained.
That is not working for me.
To test this, I have generated two MFC dialog programs: NewFailMfc1 and NewFailMfc2. Running NewFailMfc2
which accesses NewFailMfc1
’s Www
function fails when doing the new
.
// Code in NewFailMfc1.
void Www()
{
char* ch { nullptr };
ch = new char[ 100 ]; // error: attempts to allocate memory somewhere else than in the prescribed joint DLL/EXE freestore
ch[ 0 ] = '\0';
}
// Calling code in NewFailMfc2.
Www();
Does someone with a better knowledge of how DLL/EXE freestore works than me know what the problem is?
(I attempted to ask this question once before in "Global function ::operator new
fails when compiled in MyApp1
and MyApp2
. During the asking process, I discovered that the problem was occurring more generally than in the <random>
std lib.)
EDIT1:
In MSDN a nice virtual agent found Potential Errors Passing CRT Objects Across DLL Boundaries for me. Unfortunately, the only solution it recommends is compiling all your programs with the /MD
compiler option, and not /MT
which uses multiple copies of the CRT which automatically leads to crossing boundaries and memory access violations.
This is not good news for an app developer like me. What I need is a Best Practice so I can apply it and meet my delivery deadlines without having to deal with arcane low-level memory problems. How would I fx know there is a hidden call to the global ::operator new
in the std:random_device
type? I wouldn’t until it access-violated. Only now after all this research do I realize that by it calling the global new
, it was crossing a boundary which gave my DLL/EXE an access violation. Very obscure.
EDIT2:
I have submitted a bug report in Visual Studio regarding the std::random_device implementation. See "std::random_device instantiation causes an access-violation in certain cases".
Explicit Instantiation forces the compiler to generate code for a specific parameter list of a templated class or function. Without that code my imported DLL/EXE
binaries were failing on runtime instantiations like ch = new char[ 100 ]
and std::random_device rd;
which implicitly does a global ::operator new
. I have found no useful explanation for why this occurs. IPC discussions unfortunately don’t clearly distinguish between client-server runtime involving more than one running process, and runtime code that imports binary code (DLL/EXE
) that was compiled and exported elsewhere.
The solution was to add a template parameter to the class that was failing and add a .cpp file to each module that explicitly instantiated the class, like MyClsModule1.cpp
, MyClsModule2.cpp
, etc. In these files I explicitly instantiate the class for that module. I also add the .h
files, like MyClsModule1.h
, MyClsModule2.h
, for each module which contain extern
s so duplicate code generations don’t occur within a particular module. With this approach each module generates module-specific class code at compile time, which forces the module’s threads to allow heap instantiations which access that module’s process heap.
This modern C++ solution is elegant for me in that it keeps me from having to re-introduce complex IPC solutions like COM
into my app code.
From an app developer’s perspective I think that my original code ought to have worked or at least have generated errors that were more informative as to what the problem was, so I am leaving my bug report mentioned in EDIT2 in effect.