问题的灵感来自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。我不鼓励将任何重量级初始化代码移动到全局初始化阶段;但我想简单的内存分配代码足够安全(?)。
答案 0 :(得分:4)
这完全取决于你在DLLMain
内所做的事情。由于文档拒绝陈述可以做什么和不能做什么,并且由于CRT没有做出任何承诺,这总是感觉像是一个风险区域。
就个人而言,我会将所有全局初始化移动到从DLL导出的单个例程中,并坚持所有客户端在调用任何其他函数之前调用它。
答案 1 :(得分:3)
在加载DLL时初始化CRT是非常常见场景,例如,它适用于任何COM服务器。因此,您可以依赖CRT明确支持该场景,只要您不要求它使用代码来初始化您的变量,而代码又依赖于危险的api调用。初始化托管对象是一种着名的故障模式,在保存加载程序锁定时无法初始化CLR。僵局非常难以诊断,但很容易被发现。这一般都是正确的,你可以毫不费力地发现你遇到了问题。只为它寻找解决方法。
然而,通过使用主程序和使用CRT的不同实例的一个或多个DLL,可能需要付出很多代价。这是测试程序中发生的事情。您必须非常仔细地制作DLL的导出函数,以便不返回任何指针或C ++对象。你将逃避返回const char *,调用者不应该拥有该指针的所有权。据推测。