静态调用DLL
- 当系统启动使用加载时动态链接的程序时,它将使用链接器放置在文件中的信息来查找进程使用的 DLL 的名称。 然后系统搜索 DLL。
- 如果系统找不到所需的 DLL,它将终止进程并显示一个向用户报告错误的对话框。 否则,系统会将 DLL 映射到进程的虚拟地址空间,并递增 DLL 引用计数。
- 系统调用入口点函数。 函数接收一个代码,指示进程正在加载 DLL。 如果入口点函数不返回 TRUE,系统将终止进程并报告错误。
- 最后,系统使用导入的 DLL 函数的起始地址修改函数地址表。
- DLL 在初始化期间映射到进程的虚拟地址空间,并且仅在需要时才加载到物理内存中。
动态加载DLL
当应用程序调用 LoadLibrary 或 LoadLibraryEx 函数时,系统会尝试查找 DLL。 如果搜索成功,系统会将 DLL 模块映射到进程的虚拟地址空间,并递增引用计数。 如果对 LoadLibrary 或 LoadLibraryEx 的调用指定了一个 DLL,该 DLL 的代码已映射到调用进程的虚拟地址空间中,则函数仅返回 DLL 的句柄并递增 DLL 引用计数。 请注意,具有相同基本文件名和扩展名但位于不同目录中的两个 DLL 不被视为同一 DLL。
系统在调用 LoadLibrary 或 LoadLibraryEx 的线程上下文中调用入口点函数。 如果进程已通过对 LoadLibrary 或 LoadLibraryEx 的调用加载了 DLL,但没有对 FreeLibrary 函数的相应调用,则不会调用入口点函数。
如果系统找不到 DLL 或入口点函数返回 FALSE, 则 LoadLibrary 或 LoadLibraryEx 返回 NULL。 如果 LoadLibrary 或 LoadLibraryEx 成功,它将返回 DLL 模块的句柄。 进程可以使用此句柄在调用 GetProcAddress、 FreeLibrary 或 FreeLibraryAndExitThread 函数时标识 DLL。
GetModuleHandle 函数返回 GetProcAddress、FreeLibrary 或 FreeLibraryAndExitThread 中使用的句柄。 仅当 DLL 模块已通过加载时链接或先前调用 LoadLibrary 或 LoadLibraryEx 映射到进程的地址空间时,GetModuleHandle 函数才会成功。 与 LoadLibrary 或 LoadLibraryEx 不同, GetModuleHandle 不会递增模块引用计数。 GetModuleFileName 函数检索与 GetModuleHandle、LoadLibrary 或 LoadLibraryEx 返回的句柄关联的模块的完整路径。
进程可以使用 GetProcAddress 通过 LoadLibrary 或 LoadLibraryExGetModuleHandle 返回的 DLL 模块句柄获取 DLL 中导出函数的地址。
当不再需要 DLL 模块时,进程可以调用 FreeLibrary 或 FreeLibraryAndExitThread。 如果引用计数为零,这些函数会递减模块引用计数,并从进程的虚拟地址空间取消映射 DLL 代码。
运行时动态链接使进程能够继续运行,即使 DLL 不可用也是如此。 然后,该过程可以使用替代方法来实现其目标。 例如,如果一个进程找不到一个 DLL,它可以尝试使用另一个 DLL,或者它可能会通知用户出错。 如果用户可以提供缺少的 DLL 的完整路径,则进程可以使用此信息加载 DLL,即使它不在正常的搜索路径中。 这种情况与加载时链接形成鲜明对比,在该链接中,如果系统找不到 DLL,系统只会终止进程。
如果 DLL 使用 DllMain 函数对进程的每个线程执行初始化,则运行时动态链接可能会导致问题,因为不会对调用 LoadLibrary 或 LoadLibraryEx 之前存在的线程调用入口点。
DLL的数据范围
DLL可以包含全局数据或本地数据。
变量范围
编译器和链接器将 DLL 源代码文件中声明为全局的变量视为全局变量,但加载给定 DLL 的每个进程都会获取该 DLL 的全局变量的自身副本。 静态变量的范围仅限于在其中声明静态变量的块。 因此,默认情况下,每个进程都有自己的 DLL 全局变量和静态变量实例。
动态内存分配
当 DLL 使用 GlobalAlloc、 LocalAlloc、 HeapAlloc 和 VirtualAlloc) 的任何内存分配 (函数分配内存时,内存在调用进程的虚拟地址空间中分配,并且只能由该进程的线程访问。
DLL 可以使用文件映射来分配可在进程之间共享的内存。
线程本地存储
线程本地存储 (TLS) 函数使 DLL 能够分配索引,以便为多线程进程的每个线程存储和检索不同的值。 例如,每次用户打开新电子表格时,电子表格应用程序都可以创建同一线程的新实例。 为各种电子表格操作提供函数的 DLL 可以使用 TLS 保存有关每个电子表格的当前状态的信息 (行、列等) 。
Windows Server 2003 和 Windows XP: Visual C++ 编译器支持用于声明线程局部变量的语法: _declspec (线程) 。 如果在 DLL 中使用此语法,将无法在 Windows Vista 之前的 Windows 版本上使用 LoadLibrary 或 LoadLibraryEx 显式加载 DLL。 如果 DLL 将被显式加载,则必须使用线程本地存储函数,而不是 _declspec (线程) 。