DLL中声明的全局变量会怎样?

时间:2020-03-05 18:56:50  来源:igfitidea点击:

假设我用C ++编写了一个DLL,并使用一个非平凡的析构函数声明了一个类的全局对象。卸载DLL时将调用析构函数吗?

解决方案

回答

当应用程序结束或者DLL卸载时(以先到者为准),应调用它。请注意,这在某种程度上取决于我们要编译的实际运行时。

另外,请注意非平凡的析构函数,因为同时存在时序和排序问题。在析构函数所依赖的DLL之后,DLL可能会被卸载,这显然会引起问题。

回答

当调用带有fdwReason = DLL_PROCESS_DETACH参数的DllMain时,这意味着该DLL已由应用程序卸载。这是调用全局/静态对象的析构函数之前的时间。

回答

Microsoft的本页详细介绍了DLL初始化和全局变量销毁的细节:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx

回答

如果我们想查看链接.dll时执行的实际代码,请查看"%ProgramFiles%\ Visual Studio 8 \ vc \ crt \ src \ dllcrt0.c"。

从检查来看,当由dll CRT维护的内部引用计数达到零时,将通过_cexit()调用析构函数。

回答

在Windows C ++ DLL中,所有全局对象(包括类的静态成员)都将在使用DLL_PROCESS_ATTACH调用DllMain之前构造,并且在使用DLL_PROCESS_DETACH调用DllMain之后立即销毁它们。

现在,我们必须考虑三个问题:

0当然,全局非常量对象是邪恶的(但是我们已经知道,所以我将避免提及多线程,锁,上帝对象等)。

1不保证对象或者不同编译单元(即CPP文件)的构造顺序,因此,如果在两个不同的CPP中实例化两个对象,我们就不能希望在B之前构造对象A。如果B依赖于A,这一点很重要。解决方案是将所有全局对象移动到同一CPP文件中,就像在同一编译单元内部一样,对象的实例化顺序将是构造顺序(以及该顺序的相反顺序)破坏)

2 DllMain中禁止执行某些操作。这些东西也可能在构造函数中被禁止。因此,避免锁定某些东西。请参阅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,无论如何),直到我们调用它们的方法之一为止,此时它们将自行初始化。如果在主体(或者主体的后代函数之一)中使用这些对象,我们会没事的,因为在执行DllMain之后将调用它们。

3当然,如果DLL A中的某些全局对象依赖于DLL B中的全局对象,则我们应非常小心DLL的加载顺序,并因此而依赖。在这种情况下,具有直接或者间接循环依赖关系的DLL将使我们发疯。最好的解决方案是打破循环依赖。

附注:请注意,在C ++中,构造函数会抛出异常,并且我们不希望在DLL加载过程中发生异常,因此请确保没有非常充分的理由,全局对象就不会使用异常。由于正确编写的析构函数无权抛出,因此在这种情况下DLL卸载应该可以。