C++ 在 DLL 中声明的全局变量会发生什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/75701/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What happens to global variables declared in a DLL?
提问by Dima
Let's say I write a DLL in C++, and declare a global object of a class with a non-trivial destructor. Will the destructor be called when the DLL is unloaded?
假设我用 C++ 编写了一个 DLL,并声明了一个具有非平凡析构函数的类的全局对象。卸载DLL时会调用析构函数吗?
采纳答案by paercebal
In a Windows C++ DLL, all global objects (including static members of classes) will be constructed just before the calling of the DllMain with DLL_PROCESS_ATTACH, and they will be destroyed just after the call of the DllMain with DLL_PROCESS_DETACH.
在 Windows C++ DLL 中,所有全局对象(包括类的静态成员)将在使用 DLL_PROCESS_ATTACH 调用 DllMain 之前构造,并在使用 DLL_PROCESS_DETACH 调用 DllMain 之后立即销毁。
Now, you must consider three problems:
现在,你必须考虑三个问题:
0 - Of course, global non-const objects are evil (but you already know that, so I'll avoid mentionning multithreading, locks, god-objects, etc.)
0 - 当然,全局非常量对象是邪恶的(但你已经知道了,所以我将避免提及多线程、锁、上帝对象等)
1 - The order of construction of objects or different compilation units (i.e. CPP files) is not guaranteed, so you can't hope the object A will be constructed before B if the two objects are instanciated in two different CPPs. This is important if B depends on A. The solution is to move all global objects in the same CPP file, as inside the same compilation unit, the order of instanciation of the objects will be the order of construction (and the inverse of the order of destruction)
1 - 对象或不同编译单元(即 CPP 文件)的构造顺序无法保证,因此如果两个对象在两个不同的 CPP 中实例化,则不能希望对象 A 在 B 之前构造。如果 B 依赖于 A,这很重要。 解决方案是将所有全局对象移动到同一个 CPP 文件中,因为在同一个编译单元内,对象的实例化顺序将是构造顺序(以及顺序的倒数)毁灭)
2 - There are things that are forbidden to do in the DllMain. Those things are probably forbidden, too, in the constructors. So avoid locking something. See Raymond Chen's excellent blog on the subject:
2 - 在 DllMain 中有一些禁止做的事情。这些事情可能在构造函数中也是被禁止的。所以避免锁定某些东西。请参阅 Raymond Chen 关于该主题的优秀博客:
http://blogs.msdn.com/oldnewthing/archive/2004/01/27/63401.aspx
http://blogs.msdn.com/oldnewthing/archive/2004/01/27/63401.aspx
http://blogs.msdn.com/oldnewthing/archive/2004/01/28/63880.aspx
http://blogs.msdn.com/oldnewthing/archive/2004/01/28/63880.aspx
In this case, lazy initialization could be interesting: The classes remain in an "un-initialized" state (internal pointers are NULL, booleans are false, whatever) until you call one of their methods, at which point they'll initialize themselves. If you use those objects inside the main (or one of the main's descendant functions), you'll be ok because they will be called after execution of DllMain.
在这种情况下,延迟初始化可能很有趣:类保持在“未初始化”状态(内部指针为 NULL,布尔值为 false,无论如何),直到您调用它们的方法之一,此时它们将初始化自己。如果您在 main(或 main 的后代函数之一)中使用这些对象,您会没事的,因为它们将在 DllMain 执行后被调用。
3 - Of course, if some global objects in DLL A depend on global objects in DLL B, you should be very very careful about DLL loading order, and thus dependancies. In this case, DLLs with direct or indirect circular dependancies will cause you an insane amount of headaches. The best solution is to break the circular dependancies.
3 - 当然,如果 DLL A 中的某些全局对象依赖于 DLL B 中的全局对象,则您应该非常小心 DLL 加载顺序,以及依赖关系。在这种情况下,具有直接或间接循环依赖关系的 DLL 会让您非常头疼。最好的解决方案是打破循环依赖。
P.S.: Note that in C++, constructor can throw, and you doesn't want an exception in the middle of a DLL loading, so be sure your global objects won't be using exception without a very very good reason. As correctly written destructors are not authorized to throw, the DLL unloading should be ok in this case.
PS:请注意,在 C++ 中,构造函数可以抛出,并且您不希望在 DLL 加载过程中出现异常,因此请确保您的全局对象不会在没有非常充分的理由的情况下使用异常。由于正确编写的析构函数无权抛出,因此在这种情况下卸载 DLL 应该没问题。
回答by Mark Ransom
This page from Microsoft goes into the details of DLL initialization and destruction of globals:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx
Microsoft 的这个页面详细介绍了 DLL 初始化和全局变量的销毁:http:
//msdn.microsoft.com/en-us/library/988ye33t.aspx
回答by MSN
If you want to see the actual code that gets executed when linking a .dll, take a look at %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c
.
如果您想查看链接 .dll 时执行的实际代码,请查看%ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c
.
From inspection, destructors will be called via _cexit()
when the internal reference count maintained by the dll CRT hits zero.
通过检查,_cexit()
当 dll CRT 维护的内部引用计数达到零时,将调用析构函数。
回答by Philip Rieck
It should be called when either the application ends or the DLL is unloaded, whichever comes first. Note that this is somewhat dependent on the actual runtime you're compiling against.
它应该在应用程序结束或 DLL 卸载时调用,以先到者为准。请注意,这在某种程度上取决于您正在编译的实际运行时。
Also, beware non-trivial destructors as there are both timing and ordering issues. Your DLL may be unloaded aftera DLL your destructor relies on, which would obviously cause issues.
此外,请注意非平凡的析构函数,因为存在时间和顺序问题。您的 DLL 可能会在您的析构函数所依赖的 DLL之后被卸载,这显然会导致问题。
回答by INS
When DllMain with fdwReason = DLL_PROCESS_DETACH parameter is called it means the DLL is unloaded by the application. This is the time before the destructor of global/static objects gets called.
当调用带有 fdwReason = DLL_PROCESS_DETACH 参数的 DllMain 时,意味着应用程序卸载了 DLL。这是调用全局/静态对象的析构函数之前的时间。
回答by bruziuz
In windows binary image files with extension *.exe, *.dll are in PE formatSuch files have Entry Point. You can view it with dumpbin tool like
在 windows 中,扩展名为 *.exe、*.dll 的二进制映像文件为PE 格式,此类文件具有入口点。您可以使用 dumpbin 工具查看它,例如
dumpbin /headers dllname.dll
dumpbin /headers dllname.dll
If you use C runtime from Microsoft, then your entry point will be something like *CRTStartup or *DllMainCRTStartup
如果您使用 Microsoft 的 C 运行时,那么您的入口点将类似于 *CRTStartup 或 *DllMainCRTStartup
Such functions perform initialization of c and c++ runtime and delegate execution to (main, WinMain) or to DllMain respectively.
此类函数执行 c 和 c++ 运行时的初始化,并将执行分别委托给 (main, WinMain) 或 DllMain。
If you use Microsofts VC compiler then you can watch at source code of this functions in yours VC directory:
如果您使用微软的 VC 编译器,那么您可以在您的 VC 目录中查看此函数的源代码:
- crt0.c
- dllcrt0.c
- crt0.c
- dllcrt0.c
DllMainCRTStartup process all things need to init/deinit your global variables from .data sections in normal scenario, when it retrive notification DLL_PROCESS_DETACH during dll unload. For example:
DllMainCRTStartup 处理所有事情都需要在正常情况下从 .data 部分初始化/取消初始化您的全局变量,当它在 dll 卸载期间检索通知 DLL_PROCESS_DETACH 时。例如:
- main or WinMain of startup thread of program returns control flow
- you explictly call FreeLibrary and use-dll-counter is zero
- 程序启动线程的 main 或 WinMain 返回控制流
- 您明确调用 FreeLibrary 并且 use-dll-counter 为零