22.2.3 应用程序链接DLL
使用装载动态链接的进程,当进程启动时,如果需要的DLL没有找到,系统会终止进程,并提供给用户一个错误信息。此种情况下,系统不会终止使用运行时动态链接的进程,但是DLL导出的函数对应用程序来说是不可用的。有两种方法可以调用DLL中的函数。
装载时动态链接,模块清楚地调用导出DLL函数。此时,需要导入DLL库链接到模块。应用程序装载后,导入库提供装载DLL和定位导出DLL函数需要的信息的系统。
运行时动态链接,在模块运行时,使用LoadLibrary()或LoadLibraryEx()函数装载DLL。DLL装载后,模块调用GetProcAddress()函数获取导出DLL函数的地址。模块使用GetProcAddress()函数返回的函数指针调用导出DLL函数,去掉了导入库的工作。
当系统启动使用装载时动态链接的应用程序时,使用文件中的信息定位需要的DLL的名称。系统查找DLL时按照下面的顺序查找。
(1)应用程序装载的目录。
(2)当前目录。
(3)Windows系统目录,使用GetSystemDirectory()函数获取的目录路径。
(4)Windows目录,使用GetWindowsDirectory()函数获取的目录路径。
(5)在PATH环境变量中列出的目录。
如果系统不能查找到指定的DLL,则终止进程,并显示一个报告错误的对话框。否则,系统会映射DLL模块到进程的虚拟地址空间中,并增加DLL引用数量。接着系统调用入口函数。函数接收指示进程正在装载DLL的代码。如果入口函数没有返回true,系统会终止进程并报告错误。最后,系统会修改进程代码,为引用的DLL函数提供开始地址。
在初始化时,DLL被映射到进程的虚拟地址空间中,并且只有当需要时才会将其装载进物理内存中。单个应用程序调用LoadLibrary()或LoadLibraryEx()函数时,系统会使用与装载时动态链接使用的查找顺序相同的顺序试图查找DLL。如果查找到了,系统映射DLL模块到进程的虚拟地址空间,并增加引用数量。如果使用LoadLibrary()或LoadLibraryEx()函数调用的DLL,代码已经映射到调用进程的虚拟地址空间中,函数返回DLL句柄,并增加DLL的引用数量。注意两个具有相同基本文件名和扩展名但是不在同一个目录中的DLL,作为两个不同的DLL对待。
系统在调用LoadLibrary()或LoadLibraryEx()的线程的上下文中调用入口函数。如果DLL已经被进程通过LoadLibrary()或LoadLibraryEx()装载,并且没有调用相应的FreeLibrary()函数时,则系统不会调用入口函数。如果系统没有找到DLL或者入口函数返回false,LoadLibrary()或LoadLibraryEx()返回NULL。如果LoadLibrary()或LoadLibraryEx()成功,返回DLL模块的句柄。进程可以在GetProcAddress()、FreeLibrary()或者FreeLibraryAndExitThread()函数中使用句柄标识DLL。