3

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 (?).

Community
  • 1
  • 1
user396672
  • 3,106
  • 1
  • 21
  • 31
  • 1
    Leaking memory isn't a concern? – AJG85 Feb 25 '11 at 15:33
  • @AJG85: why you suspict memory leak? Of course, there are no proper cleanup in this simple example, but such cleanup may be added. – user396672 Feb 25 '11 at 15:44
  • @user I'm just being master of the obvious, it's my bad sense of humor. More to the point there is nothing saying you can't use global initialization but there are other ways. – AJG85 Feb 25 '11 at 16:16
  • Quick tip: Use four spaces indentation, not `
    ` to format code - otherwise headers such as `` are treated as HTML tags and lost. You can use the {} button in the formatting toolbar to quickly format code.
    – bdonlan Feb 25 '11 at 16:19
  • 1
    @bdonlan: thank you, i've returned inequality from "<" back to "<" :) – user396672 Feb 25 '11 at 16:39
  • Also, you can do this inline with backticks (`\``) – bdonlan Feb 25 '11 at 17:06
  • @user396672: `m[i]= *new string("some text");` makes a new string in the heap, copies it to the map, and then the string in the heap is leaked. – Mooing Duck Dec 11 '13 at 01:27

2 Answers2

4

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • @David: ... and provide inline function for particular library loading instead of direct call of Winapi LoadLibrary. Indeed, it is a good solution if library is loaded explicitly. – user396672 Feb 25 '11 at 15:52
  • @user I'm not sure what you mean. If you could clarify, I'm sure I'd be able to offer an opinion! Actually this is a very common solution. It's used by ComCtrls, COM, WinSock and many other libraries. – David Heffernan Feb 25 '11 at 15:54
  • @David: Yes, it is common practice, I mean only inlining init function to avoid creating nontrivial .lib library for the dll. – user396672 Feb 25 '11 at 16:05
  • @user Surely the init function is just an exported function like any other? Perhaps I'm just being dim - I don't actually use C++ tools all that much. – David Heffernan Feb 25 '11 at 16:10
  • @David: If it is exported I can't call it before library loading :) – user396672 Feb 25 '11 at 16:16
  • @user why would you, and how could you, want to call it before the library was loaded? – David Heffernan Feb 25 '11 at 16:18
  • @David: I think the suggestion is to have the function defined in the public header, another way is to make a .lib which contains both import libraries and code objects. – Ben Voigt Feb 25 '11 at 16:43
  • 1
    @David: I'd want to (automatically) get library loaded when user calls library initialization function. So the function itself can not be placed in the library. Instead of LoadLibrary+CallInitFunc I want simply callInitFunc and get library loaded under the hood of the init function. (I don't discuss here an implicit loading case with .lib directly linked to user's exe, only dynamic loading with LoadLibrary) – user396672 Feb 25 '11 at 16:49
2

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    so it is relatively safe if I use microsoft's crt(+microsoft stl, for instance) and don't invoke LoadLibrary or any other dangerous function during initialization? (I understand multiple runtime problems, although it is an orthogonal ussue :) – user396672 Feb 25 '11 at 16:28
  • Yes, that's what my answer says. – Hans Passant Feb 25 '11 at 16:35
  • @user How could it not be safe to include CRT with runtime linking? The issues arise when you take actions at initialization time which are in fact part of DLLMain. – David Heffernan Feb 25 '11 at 20:19
  • @David: We discuss the initialization time, i.e. DllMain and global scope initializers. Dynamic linking in my test program is a way to delay crt initialization as late as possible. I see the only reason not to initialize crt before DllMain: possible problems with "recursive" loading and initializing dll's, so dynamic crt linking is the most risky case (and my test covers this case). – user396672 Feb 28 '11 at 12:48