从DllMain或全局初始化程序调用c ++运行时

时间:2011-02-25 15:25:11

标签: c++ windows visual-c++ dll

问题的灵感来自this discussion 似乎对DllMain(或来自全局变量ctor)的C ++运行时调用的担忧已经过时了。我经常在dll中使用全局初始化器而没有任何错误,现在我运行了一个特殊的测试程序(用VC2010 Express w / o SP编译),其中包含带有静态运行时链接的exe模块和带有动态链接的dll。 Dll是由LoadLibrary()从exe手动加载的 Dll在全局初始化期间创建并填充地图对象(因此使用运行时库,至少是内存分配函数)。 Dll代码:

#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代码(用于Release配置;将运行时库名称更改为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;
}

输出符合预期:

before LoadLibrary: CRT NOT loaded by .exe module
after LoadLibrary: CRT Loaded
map[99]: some text

没有失败,nullpointers,pagefaults等。

使用DependencyWalker进行性能分析也确认运行时lib(MSVCR100.DLL)仅在LoadLibrary调用之后加载(并且不是由exe预加载和初始化)。

在全局初始化阶段之前,似乎在dll_example.dll加载过程中正确加载并初始化了动态运行时库。

有什么想法吗?

PS。我不鼓励将任何重量级初始化代码移动到全局初始化阶段;但我想简单的内存分配代码足够安全(?)。

2 个答案:

答案 0 :(得分:4)

这完全取决于你在DLLMain内所做的事情。由于文档拒绝陈述可以做什么和不能做什么,并且由于CRT没有做出任何承诺,这总是感觉像是一个风险区域。

就个人而言,我会将所有全局初始化移动到从DLL导出的单个例程中,并坚持所有客户端在调用任何其他函数之前调用它。

答案 1 :(得分:3)

在加载DLL时初始化CRT是非常常见场景,例如,它适用于任何COM服务器。因此,您可以依赖CRT明确支持该场景,只要您不要求它使用代码来初始化您的变量,而代码又依赖于危险的api调用。初始化托管对象是一种着名的故障模式,在保存加载程序锁定时无法初始化CLR。僵局非常难以诊断,但很容易被发现。这一般都是正确的,你可以毫不费力地发现你遇到了问题。只为它寻找解决方法。

然而,通过使用主程序和使用CRT的不同实例的一个或多个DLL,可能需要付出很多代价。这是测试程序中发生的事情。您必须非常仔细地制作DLL的导出函数,以便不返回任何指针或C ++对象。你将逃避返回const char *,调用者不应该拥有该指针的所有权。据推测。