The question is inspired by this discussion.
It seems that fears concerning C++ runtime invokations from DllMain (or from global variables ctor's) are sligtly outdated. I'm using global initializers in dlls frequently without any faults, and now I've run a special test program (compiled with VC2010 Express w/o SP) containing exe module with static runtime linkage and dll with dynamic one. Dll is manualy loaded from exe by LoadLibrary().
Dll creates and fills a map object during global initialization (and therefore uses runtime library, at least memory allocatiion functions).
Dll code:
#include <map>
using namespace std;
struct A{
char* p;
static const int num=1000;
map<int,string> m;
A(){
for(int i=0; i<num; ++i){m[i]= *new string("some text");}
}
};
A a;
extern "C"{
_declspec(dllexport) const char* getText(int i){ return a.m[i].data(); }
}
Exe code (for Release configuration; change runtime library name to MSVCR100D.DLL for Debug):
#include <windows.h>
typedef const char* (*pfunc_t)(int idx);
int main(int argc, TCHAR* argv[])
{
HMODULE h_crt= GetModuleHandle("MSVCR100.DLL");
// ensure that runtime library is NOT loaded yet:
MessageBox(NULL,(NULL==h_crt)? "CRT NOT loaded by .exe module": "CRT Loaded by .exe module" ,"before LoadLibrary",MB_OK);
HMODULE hlib=LoadLibrary("dll_example.dll");
h_crt= GetModuleHandle("MSVCR100.DLL");
MessageBox(NULL,(NULL==h_crt)? "CRT NOT loaded": "CRT Loaded" ,"after LoadLibrary",MB_OK);
pfunc_t pfunc= (pfunc_t)(void*)GetProcAddress(hlib,"getText");
MessageBox(NULL,pfunc(99),"map[99]",MB_OK);
return 0;
}
The output is as expected:
before LoadLibrary: CRT NOT loaded by .exe module
after LoadLibrary: CRT Loaded
map[99]: some text
No failures, nullpointers, pagefaults, etc.
Profiling with DependencyWalker also confirms that runtime lib(MSVCR100.DLL) is loaded only after LoadLibrary call (and is not preloaded and initialized by exe).
It seems that dynamic runtime library is loaded and initialized correctly during the dll_example.dll loading process before global initialization phase.
any thoughts?
PS. I don't encourage to move any heavyweight initialization code to global init phase; but I suppose that simple memory allocation code is safe enough (?).
It all depends what you do inside DLLMain. Since the documentation refuses to state what can and cannot be done, and since the CRT doesn't make any promises, this always feels like a risky area.
Personally I would move all my global initialization into a single routine which is exported from the DLL and insist that all clients call this before calling any other function.
Getting the CRT initialized while loading a DLL is a very common scenario, it happens for any COM server for example. As such you can rely on the CRT explicitly supporting the scenario, as long as you don't require it to initialize your variables with code that in turn depend on the dangerous api calls. Getting managed objects initialized is a famous failure mode, the CLR cannot be initialized while the loader lock is held. The deadlock is very ugly to diagnose but very easy to detect. Which is in general true, you have no trouble finding out that you got a problem. Only finding a workaround for it.
There is however plenty of hell to pay by having the main program and one of more DLLs using different instances of the CRT. Which is what is happening in your test program. You have to very carefully craft the exported functions of the DLL to not return any pointers or C++ objects. You'll get away with returning const char*, the caller isn't supposed to take ownership of that pointer. Presumably.
Related
I am writing a XLL (using XLW library) that calls a DLL function. This DLL function will get a vector reference, modify the vector and return it by argument.
I have a VS10 solution with several c++ projects, some DLLs and a XLL that will call DLL functions from excel. I compiled everything using VS10 compiler, with _HAS_ITERATOR_DEBUGGING=0 and _CRT_SECURE_NO_WARNINGS and used same runtime library (/MDd) for all projects.
I also had to rebuild the XLW library to comply with _HAS_ITERATOR_DEBUGGING=0 that I have to use in my projects.
When calling the xll_function I was getting Heap Corruption errors and couldn't figure out why.
After I tried resizing my vector before calling the dll function the error was gonne. That is, I can call the function and get the right vector returned by argument and no heap corruptions.
Could someone shed some light on this?
As I am new to using DLLs I'm not sure if this should happen or if I am doing something wrong.
As you can see in the code below, the dll function will try to resize forwards and that is the point that I think is generating the heap errors.
I'm trying to understand why this happens and how this resizing and allocation works for dlls. Maybe I can't resize a vector allocated in another heap.
** Code below - the first function is a static method in a class from a dll project and the second function is exported to the XLL.
void dll_function(double quote, const std::vector<double>& drift, const std::vector<double>& divs, std::vector<double>& forwards)
{
size_t size = drift.size();
forwards.resize(size);
for( size_t t = 0; t < size; t++)
{
forwards[t] = (quote - divs[t]) * drift[t];
}
}
MyArray xll_function(double quote, const MyArray& drift, const MyArray& divs)
{
// Resizing the vector before passing to function
std::vector<double> forwards(drift.size());
dll_function(quote, drift, divs, forwards);
return forwards;
}
To pass references to std::vector or other C++ collections across DLL boundaries, you need to do following.
Use same C++ compiler for both modules, and same version of the compiler.
In project settings, set up same value to the setting General / Platform Toolset.
In project settings, set up C/C++ / Code Generation / Runtime Library value to “Multi-threaded DLL (/MD)”, or Multi-threaded Debug DLL (/MDd) for debug config. If one of the projects have a dependency which requires static CRT setting, sorry you’re out of luck, it won’t work.
Use same configuration in both sides: if you’ve built debug version of the DLL, don’t link with release version of the consuming EXE. Also don’t change preprocessor defines like _ITERATOR_DEBUG_LEVEL or _SCL_SECURE_NO_WARNINGS, or if you did, change them to the same value for both projects.
The reason for these complications, C++ doesn’t have standardized ABI. The memory layout of std::vector and other classes changes based on many things. Operators new and delete are also in C++ standard library, i.e. you can’t allocate memory with C++ in one module, free in different one.
If you can’t satisfy these conditions, there’re several workarounds, here’s a nice summary: https://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL
Can I use C++ SetDllDirectory and LoadLibrary commands inside a C++ DLL to load another DLL? I have tried using them like this:
Executable calls the 1st DLL,
then 1st DLL loads the 2nd DLL,
then 2nd DLL does the calculations...
but when I run the executable, I get this error message:
This application has requested the Runtime to terminate it in an unusual way. Please contact the applications support team for more information.
2nd DLL works fine when linked directly to Executable!
This is the code inside my executable:
#include <windows.h>
#include <iostream>
int main(){
HINSTANCE hDLL_Link=NULL;
SetDllDirectory((LPCWSTR)L"C:\\Users\\MC\\Documents\\2014_07_01a_FDD_VC2008\\test_call_DLL\\EXE_calls_Link_DLL\\Release");
hDLL_Link=LoadLibrary((LPCWSTR)L"Link_DLL.dll");
if(hDLL_Link==NULL) std::cout<<"did not load"<<'\n';
typedef void (*Ptr_OPS_Link)();
Ptr_OPS_Link Ptr_OPS_Link_0;
Ptr_OPS_Link_0=(Ptr_OPS_Link)GetProcAddress(hDLL_Link,"OPS_Link");
Ptr_OPS_Link_0();
FreeLibrary(hDLL_Link);
system("pause");
}
this is the code inside the 1st DLL:
#include "Link.h"
extern "C" __declspec(dllexport)
void OPS_Link(){
Link*Link_Ptr_Object=NULL;
if(Link_Ptr_Object==NULL){
Link_Ptr_Object=new Link();
}
if(Link_Ptr_Object==NULL){
//can not throw inside __declspec(dllexport) functions marked extern "C" that's why std::cout is implemented:
std::cout<<"Error: could not link to FDD DLL"<<'\n';
system("pause");
}
delete Link_Ptr_Object;
Link_Ptr_Object=NULL;
}
Link::Link()
:m_void_Ptr_ObjectByDLL(NULL){
HINSTANCE hDLL=NULL;//handle to DLL
SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
hDLL=LoadLibrary((LPCWSTR)L"C:\\Users\\MC\\Documents\\2014_07_01a_FDD_VC2008\\Executable\\Release\\FDD_DLL.dll");
if(hDLL==NULL){
throw "DLL loading could not be done";
}else if(hDLL!=NULL){
typedef void (*Ptr_OPS_FDD)(std::string, int, int);
Ptr_OPS_FDD Ptr_OPS_FDD_0;//pointer to procedure inside DLL
Ptr_OPS_FDD_0=NULL;
Ptr_OPS_FDD_0=(Ptr_OPS_FDD)GetProcAddress(hDLL,"OPS_FDD");
if(Ptr_OPS_FDD_0==NULL){
FreeLibrary(hDLL);
throw "DLL exported function address could not be determined";
}else{
//run the procedure inside DLL:
Ptr_OPS_FDD_0("FDD_INPUT_Truss_Bridge_Data2_Ambient_Inch_11Channels_77824Samples_SamplingFreq_256Hz.txt",11,256);//LabScaleTruss
//Ptr_OPS_FDD_0("FDD_INPUT_Ambient_EW_15Channels_3000Samples_SamplingFreq_20Hz.txt",15,20);//AmbientEW
//Ptr_OPS_FDD_0("FDD_INPUT_Meriden_3Channels(3_5_8)_3686400Samples_SamplingFreq_2048Hz.txt",3,2048);//MeridenBridge
FreeLibrary(hDLL);
}
}
}
There are a few things in your code that can cause failure:
You do not exit if the DLL cannot be loaded:
You are passing objects that internally use dynamic allocation, and thus will use the heap manager.
For 1. above, your main() function only does a simple cout if the library cannot be found. However instead of exiting, the main function proceeds as if the library was found.
For 2. above, passing std::string as a parameter to a DLL function is error prone and not recommended unless you know exactly what you're doing. The reason it is error prone is
The DLL that contains the function call may have been built with a different set of options than the DLL that calls the function. These differing options could cause a difference in the way that std::string is implemeted, how it's layed out in memory, etc.
The DLL that contains the function call may have been built by a different version of the compiler than the DLL that calls the function. Again, same issue with differing implementations of std::string
The DLL's and modules using std::string may not have been built using the DLL version of the C runtime library. If the DLL's/modules are not built and linked using the DLL version of the runtime library, the DLL will be using a different heap than the module. Any operation on std::string will be invalid, due to differing memory heaps being used.
So in a nutshell, unless you can guarantee that
You are building the modules and DLL's with the exact same version of the compiler and compiler options.
You are linking all modules to the DLL version of the runtime library.
Then passing std::string as a parameter, and in general, passing any object that maintains dynamically allocated memory, may or will lead to runtime errors.
Besides the inadequate error handling and using the standard library across module boudaries ,there are two other things to consider.
Can I use SetDllDirectory in a dll to ... ?
Yes you can ,but you SHOULDN'T ! (BUGS waiting to happen).
Why ? because the only entity that is responsable for changing environment is the main-application.
Library code (static or dll) doesn't know in which application it's going to be used.
It might work correctly in some programs and it may fail in others.
Can I use C++ LoadLibrary/FreeLibrary in a dll to ... ?
Yes you can ,but don't use them in the dllmain function since it can deadlock your program.
I solved the problem, and showed how here:
I changed the code inside executable and 1st DLL like below, to consider error handling, and also I added the "return 0;" now the executable links to 1st DLL and it works perfect... Actually the problem was that main needed to return something...I raplaced all the "std::string" with "char*" at the DLL boundaries...By the way, the reason that I want to develop two DLLs and I'm using "SetDllDirectory" inside the 1st one is that I want to call a DLL with a C# GUI, and the problem is that there is no "SetDllDirectory" command available in C#, therefore, I came up with the idea of developing two DLLs, inside first DLL, I will use "SetDllDirectory" to take care of the required dependencies (DLL is dependent on Octave and Octave Bin directory) and then I developed a 2nd DLL which carries out the actual computations...I know that there are some methods like "[DllImport("Kernel32.dll")]" and from there we can use "SetDllDirectory" in C# but that method looks painful.
The corrected code inside executable:
#include <windows.h>
#include <iostream>
int main(){
try{
HINSTANCE hDLL_Link=NULL;
hDLL_Link=LoadLibrary((LPCWSTR)L"Link_DLL.dll");
if(hDLL_Link==NULL){
throw "Link DLL did not load";
}else{
typedef void (*Ptr_OPS_Link)();
Ptr_OPS_Link Ptr_OPS_Link_0=NULL;
Ptr_OPS_Link_0=(Ptr_OPS_Link)GetProcAddress(hDLL_Link,"OPS_Link");
if(Ptr_OPS_Link_0==NULL){
throw "Link DLL exported function not found";
FreeLibrary(hDLL_Link);
}else{
Ptr_OPS_Link_0();
FreeLibrary(hDLL_Link);
}
}
}
catch(char*char_Ptr_Exception){
std::cerr<<"Error: "<<char_Ptr_Exception<<'\n';
}
system("pause");
return 0;
}
The corrected code inside 1st DLL:
#include "Link.h"
extern "C" __declspec(dllexport)
void OPS_Link(){
Link*Link_Ptr_Object=NULL;
if(Link_Ptr_Object==NULL){
Link_Ptr_Object=new Link();
}
if(Link_Ptr_Object==NULL){
////can not throw inside __declspec(dllexport) functions marked extern "C" that's why std::cout is implemented:
//std::cout<<"Error: could not link to FDD DLL"<<'\n';
system("pause");
}
delete Link_Ptr_Object;
Link_Ptr_Object=NULL;
}
Link::Link()
:m_void_Ptr_ObjectByDLL(NULL){
HINSTANCE hDLL=NULL;//handle to DLL
SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
//path relative to executable (C# executable or C++ executable)
hDLL=LoadLibrary((LPCWSTR)L"FDD_DLL.dll");
if(hDLL==NULL){
throw "FDD DLL did not load";
}else if(hDLL!=NULL){
typedef void (*Ptr_OPS_FDD)(char*, int, int);
Ptr_OPS_FDD Ptr_OPS_FDD_0;//pointer to procedure inside DLL
Ptr_OPS_FDD_0=NULL;
Ptr_OPS_FDD_0=(Ptr_OPS_FDD)GetProcAddress(hDLL,"OPS_FDD");
if(Ptr_OPS_FDD_0==NULL){
throw "FDD DLL exported function not found";
FreeLibrary(hDLL);
}else{
//run the procedure inside DLL:
Ptr_OPS_FDD_0("FDD_INPUT_Truss_Bridge_Data2_Ambient_Inch_11Channels_77824Samples_SamplingFreq_256Hz.txt",11,256);//LabScaleTruss
//Ptr_OPS_FDD_0("FDD_INPUT_Ambient_EW_15Channels_3000Samples_SamplingFreq_20Hz.txt",15,20);//AmbientEW
//Ptr_OPS_FDD_0("FDD_INPUT_Meriden_3Channels(3_5_8)_3686400Samples_SamplingFreq_2048Hz.txt",3,2048);//MeridenBridge
FreeLibrary(hDLL);
}
}
}
C++ class constructor can be inlined or not be inlined. However, I found a strange situation where only inline class constructor can avoid Visual Studio memory crash. The example is as follows:
dll.h
class _declspec(dllexport) Image
{
public:
Image();
virtual ~Image();
};
class _declspec(dllexport) Testimage:public Image
{
public:
Testimage();
virtual ~Testimage();
};
typedef std::auto_ptr<Testimage> TestimagePtr;
dll.cpp
#include "dll.h"
#include <assert.h>
Image::~Image()
{
std::cout<<"Image is being deleted."<<std::endl;
}
Image::Image()
{
}
Testimage::Testimage()
{
}
Testimage::~Testimage()
{
std::cout<<"Geoimage is being deleted."<<std::endl;
}
The dll library is compiled as a dynamic library, and it is statically linked to the C++ runtime library (Multi-threaded Debug (/MTd)). The executable program that runs the library is as follows:
int main()
{
TestimagePtr my_img(new Testimage());
return 0;
}
The executable program will invoke the dll library and it also statically links the runtime library. The problem I have is that when running the executable program the following error message appears:
However, when the class constructor in dll is inlined as the following codes show:
class _declspec(dllexport) Image
{
public:
Image();
virtual ~Image();
};
class _declspec(dllexport) Testimage:public Image
{
public:
Testimage()
{
}
virtual ~Testimage();
};
The crash will disappear. Could someone explain the reason behind? Thanks! By the way, I am using VC2010.
EDIT: The following situation also trigger the same crash
.
Situation 1
int main()
{
//TestimagePtr my_img(new Testimage());
Testimage *p_img;
p_img = new Testimage();
delete p_img;
return 0;
}
it is statically linked to the C++ runtime library (Multi-threaded Debug (/MTd)
This is a very problematic scenario in versions of Visual Studio prior to VS2012. The issue is that you have more than one version of the CRT loaded in your process. One used by your EXE, another used by the DLL. This can cause many subtle problems, and not so subtle problems like this crash.
The CRT has global state, stuff like errno and strtok() cannot work properly when that global state is updated by one copy of the CRT and read back by another copy. Relevant to your crash, a hidden global state variable is the heap that the CRT uses to allocate memory from. Functions like malloc() and ::operator new use that heap.
This goes wrong when objects are allocated by one copy of the CRT and released by another. The pointer that's passed to free() or ::operator delete belongs to the wrong heap. What happens next depends on your operating system. A silent memory leak in XP. In Vista and up, you program runs with the debug version of the memory manager enabled. Which triggers a breakpoint when you have a debugger attached to your process to tell you that there's a problem with the pointer. The dialog in your screenshot is the result. It isn't otherwise very clear to me how inlining the constructor could make a difference, the fundamental issue however is that your code invokes undefined behavior. Which has a knack for producing random outcomes.
There are two approaches available to solve this problem. The first one is the simple one, just build both your EXE and your DLL project with the /MD compile option instead. This selects the DLL version of the CRT. It is now shared by both modules and you'll only have a single copy of the CRT in your process. So there is no longer a problem with having one module allocating and another module releasing memory, the same heap is used.
This will work fine to solve your problem but can still become an issue later. A DLL tends to live a life of its own and may some day be used by another EXE that was built with a different version of the CRT. The CRT will now again not be shared since they'll use different versions of the DLL, invoking the exact same failure mode you are seeing today.
The only way to guarantee that this cannot happen is to design your DLL interface carefully. And ensure that there will never be a case where the DLL allocates memory that the client code needs to release. That requires giving up on a lot of C++ goodies. You for example can never write a function that returns a C++ object, like std::string. And you can never allow an exception to cross the module boundary. You are basically down to a C-style interface. Note how COM addresses this problem by using interface-based programming techniques and a class factory plus reference counting to solve the memory management problem.
VS2012 has a counter-measure against this problem, it has a CRT version that allocates from the default process heap. Which solves this particular problem, not otherwise a workaround for the global state issue for other runtime functions. And adds some new problems, a DLL compiled with /MT that gets unloaded that doesn't release all of its allocations now causes an unpluggable leak for example.
This is an ugly problem in C++, the language fundamentally misses an ABI specification that addresses problems like this. The notion of modules is entirely missing from the language specification. Being worked on today but not yet completed. Not simple to do, it is solved in other languages like Java and the .NET languages by specifying a virtual machine, providing a runtime environment where memory management is centralized. Not the kind of runtime environment that excites C++ programmers.
I tried to reproduce your problem in VC2010 and it doesn't crash. It works with a constructor inline or not. Your problem is probably not in what you write here.
Your project is too hard to open as it seams to have its file pathes set in absolute, probably because generated with CMake. (So the files are not found by the compiler).
The problem I see in your code is that you declare the exported classes with _declspec(dllexport) directly written.
You should have a #Define to do this, and the value should be _declspec(dllimport) when read from the exe compilation. Maybe the problem comes from that.
Consider we have a dynamic library ("HelloWorld.dll") which 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 ("LoadLibraryExample.exe") which dynamically loads this DLL using 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 fine when being linked with a dynamic runtime library (/MD or /MDd switches).
The problem appears when I link them (the library and the executable) with a debug version of static runtime library (/MTd switch). The program seems to work ("Hello, World!" is displayed in the console window), but then crashes 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 magically does not appear with a release version of static runtime library (/MT switch). My assumption is that the release version just doesn't see the error, but it is still there.
After a small research I found this page on MSDN which states the following:
Using the statically linked CRT implies that any state information saved by the C runtime library will be local to that instance of the CRT.
Because a DLL built by linking to a static CRT will have its own CRT state, it is not recommended to link statically to the CRT in a DLL unless the consequences of this are specifically desired and understood.
So the library and the executable have their own copies of CRT which have their own states. An instance of std::string is constructed in the library (with some internal memory allocations being made by the library's CRT) and then returned to the executable. The executable displays it and then calls its destructor (leading to deallocation of the internal memory by the executable's CRT). As I understand, this is where an error occurs: the underlying memory of std::string is allocated with one CRT and tried to be deallocated with another one.
The problem does not appear if we return a primitive type (int, char, float, etc) or a pointer from the DLL, because there are no memory allocations or deallocations in these cases. However, an attempt to delete the returned pointer in the executable results in the same error (and not deleting the pointer obviously results in a memory leak).
So the question is: Is it possible to work around this issue?
P.S.: I really don't want to have a dependency on MSVCR100.dll and make the users of my application to install any redistributable packages.
P.P.S: The code above 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 resolved by removing extern "C" from the library function declaration:
__declspec(dllexport) std::string hello_world()
and changing the GetProcAddress call as following:
GetProcAddress(library, "?hello_world##YA?AV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##XZ")
(function name gets decorated by the C++ compiler, the actual name can be retrieved with dumpbin.exe utility). The warning is then gone but the problem remains.
P.P.P.S: I see a possible solution in providing a pair of functions in the library for every such situation: one that returns a pointer to some data and the other that deletes a pointer to these data. In this case, the memory is allocated and deallocated with the same CRT. But this solution seems very ugly and non-friendly, as we must always operate with pointers and moreover a programmer must always remember to call a special library function to delete a pointer instead of simply using a delete keyword.
Yes, this is the primary reason that /MD exists in the first place. When you build the DLL with /MT, it will get its own copy of the CRT embedded. Which creates its own heap to allocate from. The std::string object you return will be allocated on that heap.
Things go wrong when the client code tries to release that object. It calls the delete operator and that tries to release the memory on its own heap. On Vista and Win7, the Windows memory manager notices that it is asked to release a heap block that's not part of the heap and that a debugger is attached. It generates an automatic debugger break and a diagnostic message to tell you about the problem. Very nice btw.
Clearly /MD solves the problem, both your DLL and the client code will use the same copy of the CRT and thus the same heap. It is not a sure-fire solution, you will still run into trouble with the DLL is built against a different version of the CRT. Like msvcr90.dll instead of msvcr100.dll.
The only complete error free solution is to restrict the API you expose from the DLL. Don't return any pointers to any objects that need to be released by the client code. Assign ownership of objects to the module that created it. Reference counting is a common solution. And if you have to then use a heap that's shared by all code in a process, either the default process heap (GlobalAlloc) or the COM heap (CoTaskMemAlloc) qualify. Also don't allow exceptions to cross the barrier, same problem. The COM Automation abi is a good example.
I have a dynamic library of c++ code that is cross platform and mostly just native c++. I then use this dynamic library from my main exe. Up until now all has been good on OSX using gcc. Now I'm on windows I am confused as to what method I should use to enter the dll. I don't have a DllMain function at present as this wasn't required in gcc (to my knowledge). My initial tests worked but on inspection revealed that strangely one of my class constructors was being called on dll load, so I figured I needed to do something more on windows. So do I:
add a DllMain function?
am I safe to just use the noentry compiler option?
When I do either of the above I start getting compiler complaints in the vein of ".CRT section exists there may be unhandled static initializers or terminators"
I have read up on this using this article, but any advice and clarity on the best way forward would be greatly appreciated. Its all a bit blurry in my head as to what I need to do.
Based on the .CRT error, you definitely need a DllMain function. For most Windows compilers, a DllMain will be provided for you automatically, so that you don't need to write one yourself. Based on other parts of your question it seems most likely that you are using Visual C++, whose CRT does provide a DllMain for you. So while you do need a DllMain, you don't need to write the code for it.
The default VC CRT DllMain is used to initialize the CRT for the DLL in question, and to initialise all the static/global variables that the dll provides.
The model for DLLs on Unix and Windows is significantly different, and you should think of each DLL as having a more 'private' set of state. Although, if all Dlls opt into the same version of the CRT dll, some of that state will then be shared.
Because the CRT is providing a DllMain for you, you should not throw /noentry on the linker.
The .CRT section exists error (which you must have seen by throwing /noentry) is telling you that you need a DllMain because you've got one or more objects in your DLL that require static initialisation.
Martyn
If it is just a library, then NOENTRY should suffice. DllMain is there to control events that happen with the DLL (i.e. attach, detach etc).
You can change the code (slightly) to avoid all entry points but main. Essentially, if you have any variable defined outside of the functions (globally but not statically linked), wrap them in a function call. Use the often-forgotten static function variables.
Ie, change global declaration
SomeType var_name;
to this:
SomeType & var_name(){static SomeType var; return var;}
Similarly, you can change static class instance variables by changing this:
struct Container{
Container();
static Container instance;
};
Container Container::instance;
to this:
struct Container{
Container();
static Container & instance();
};
Container & Container::instance(){
static Container var;
return var;
}
This is essentially a singleton, but there might be some concurrency issues if the 1st time you access the instance will be from a multi-threaded environment. In fact, the thing to keep in mind is that unlike globally-defined variables, static locally-defined variables will be initialized the first time the function is called.