运行时库行为

更新:2007 年 11 月

C/C++ 运行时库代码执行 DLL 启动序列,从而不必像 Windows 3.x 中那样必须链接到单独的模块。C/C++ 运行时库代码中包含的是名为 _DllMainCRTStartup 的 DLL 入口点函数。_DllMainCRTStartup 函数执行若干操作,其中包括调用 _CRT_INIT,此操作初始化 C/C++ 运行时库并在静态非局部变量上调用 C++ 构造函数。如果没有此函数,运行时库将保持未初始化状态。_CRT_INIT 既可以用于静态链接的 CRT,也可以从用户 DLL 链接到 CRT DLL Msvcr90.dll。

虽然可以使用 /ENTRY: 链接器选项指定其他入口点函数,但不建议这样做,因为新的入口点函数将不得不重复 _DllMainCRTStartup 执行的所有操作。用 Visual C++ 生成 DLL 时,系统自动链接 _DllMainCRTStartup,您无需使用 /ENTRY: 链接器选项指定入口点函数。

除了初始化 C 运行时库外,_DllMainCRTStartup 还调用名为 DllMain 的函数。根据生成的 DLL 类型的不同,Visual C++ 为您提供 DllMain 并使它被链接,以便 _DllMainCRTStartup 始终有东西可以调用。这样,如果不需要初始化 DLL,则在生成 DLL 时没有什么特别的事情要做。如果需要初始化 DLL,添加代码的位置取决于编写的 DLL 类型。有关更多信息,请参见初始化 DLL

C/C++ 运行时库代码在静态非局部变量上调用构造函数和析构函数。例如,在以下 DLL 源代码中,Equus 和 Sugar 是 CHorse 类的两个静态非本地对象,它们都是在 Horses.h 中定义的。由于这些对象是在所有函数的外部定义的,源代码中没有任何函数包含对 CHorse 的构造函数或对析构函数的调用。因此,必须由运行时代码执行对这些构造函数和析构函数的调用。应用程序的运行时库代码也执行此函数。

#include "horses.h"

CHorse  Equus( ARABIAN, MALE );
CHorse  Sugar( THOROUGHBRED, FEMALE );

BOOL    WINAPI   DllMain (HANDLE hInst, 
                            ULONG ul_reason_for_call,
                            LPVOID lpReserved)
...

每当新进程尝试使用 DLL 时,操作系统就为 DLL 数据创建一个单独的副本:这称作“进程附加”。DLL 的运行时库代码调用所有全局对象的构造函数(如果有的话),然后通过选定进程附加来调用 DllMain 函数。相反的情况是线程分离:运行时库代码通过选定进程分离来调用 DllMain,然后调用一系列终止函数,其中包括 atexit 函数、全局对象的析构函数和静态对象的析构函数。请注意,进程附加中的事件顺序与进程分离中的相反。

线程附加和线程分离时也会调用运行时库代码,但是运行时库代码不主动执行任何初始化和终止操作。

您希望做什么?

请参见

概念

DLL