在DLL中声明的全局variables会发生什么?
比方说,我用C ++编写了一个DLL,并用一个不重要的析构函数声明了一个类的全局对象。 当DLL被卸载时会调用析构函数吗?
在Windows C ++ DLL中,所有全局对象(包括类的静态成员)将在DLL_PROCESS_ATTACH调用DllMain之前被构造,并且在DLL_PROCESS_DETACH调用DllMain之后被破坏。
现在,你必须考虑三个问题:
0 – 当然,全局非const对象是邪恶的(但你已经知道,所以我会避免提到multithreading,锁,上帝对象等)
1 – 构造对象或不同编译单元(即CPP文件)的顺序不能保证,因此如果两个对象在两个不同的CPP中实例化,则不能希望在B之前构造对象A. 如果B依赖于A,这一点很重要。解决方法是将所有全局对象移动到同一个CPP文件中,就像在同一个编译单元中一样,对象的实例顺序将是构build顺序(和顺序的逆顺序的破坏)
2 – 在DllMain中有些东西是被禁止的。 那些东西在施工人员中也许是被禁止的。 所以避免locking某些东西 请参阅Raymond Chen关于此主题的精彩博客:
http://blogs.msdn.com/oldnewthing/archive/2004/01/27/63401.aspx
http://blogs.msdn.com/oldnewthing/archive/2004/01/28/63880.aspx
在这种情况下,惰性初始化可能会很有趣:在您调用其中一个方法之前,这些类将保持“未初始化”状态(内部指针为NULL,布尔值为false),然后初始化它们。 如果你在main(或者main的后代函数之一)里面使用这些对象,你会没问题的,因为在执行DllMain之后会调用它们。
3 – 当然,如果DLL A中的某些全局对象依赖于DLL B中的全局对象,则应该非常小心DLL的加载顺序,从而依赖于它。 在这种情况下,具有直接或间接循环依赖关系的DLL会导致您头痛不已。 最好的解决办法是打破循环依赖。
PS:请注意,在C ++中,构造函数可以抛出,并且不希望在加载DLL的过程中出现exception,所以确保全局对象不会在没有很好理由的情况下使用exception。 由于正确写入的析构函数没有被授权抛出,在这种情况下DLL卸载应该是可以的。
来自微软的这个页面详细介绍了DLL初始化和全局variables的破坏:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx
如果您想查看链接.dll时执行的实际代码,请查看%ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c
。
从检查,当dll CRT维护的内部引用计数达到零时,将通过_cexit()
调用析构函数。
当应用程序结束或卸载DLL时,应该调用它,以先到者为准。 请注意,这有些依赖于您正在编译的实际运行时间。
此外,请注意非平凡的析构函数,因为它们都有时序和顺序问题。 你的DLL可能会在你的析构函数依赖的DLL 之后被卸载,这显然会导致问题。
当调用fdwReason = DLL_PROCESS_DETACH参数的DllMain时,意味着应用程序卸载了DLL。 这是全局/静态对象的析构函数被调用之前的时间。
在windows的二进制图像文件的扩展名* .exe,* .dll是在PE格式这样的文件有入口点。 你可以用dumpbin工具来查看它
dumpbin /头文件dllname.dll
如果您使用Microsoft的C运行时,那么您的入口点就像* CRTStartup或* DllMainCRTStartup
这些函数执行c和c ++运行时的初始化,并将执行委托给(main,WinMain)或DllMain。
如果你使用Microsofts VC编译器,那么你可以在你的VC目录下观看这个函数的源代码:
- crt0.c
- dllcrt0.c
DllMainCRTStartup处理所有需要在正常情况下从.data节点初始化/取消初始化全局variables,当它在DLL卸载期间检索DLL_PROCESS_DETACH时发出通知。 例如:
- 程序启动线程的main或WinMain返回控制stream
- 你明确地调用FreeLibrary和use-dll-counter为零