Mecanim 发表于 2023-2-15 13:27

Windows下Dll的加载方式及错误处理

Dll可以说是Windows下面相当常用的文件类型了。从功能上来讲,它相当于一个可执行程序,它是经过编译和链接而生成的,里面封装好了各种导出给用户的函数或者类。
这篇文章不会讲dll里面是什么样的格式,也不会介绍如何写一个dll。我主要想分享一下,dll加载的几种方式,以及加载dll失败的几个原因。
1. dll加载方式

    dll加载方式大致可以分为3类:静态加载、动态加载和延迟加载。
    所谓静态加载是说,dll的加载发生在程序main函数启动前。这个加载行为是由crt做的。你所需要做的所有事情就是把编译dll时生成的lib链接上就可以了。如果你的dll路径中缺少这个dll,那么你将会得到一个类似于下面这样的错误:


当你点了确定之后,程序将直接退出,你没有办法在你的main函数中处理它。
    动态加载是说,使用LoadLibrary或者LoadLibraryEx来加载一个dll。当dll加载成功时,你会得到一个非空的HMODULE。接下来,你可以使用GetProcAddress来获取这个HMODULE中的导出接口了。你可以理解成,静态链接的lib其实是帮我们在很早的时候就完成了这些事情。使用LoadLibrary有一些细节需要注意。首先是路径,它会在一些特定的路径寻找dll,如果没有找到则会报错。当成功加载了dll后,crt会初始化dll中的全局变量,并且HMODULE的引用计数会+1。如果LoadLibrary多次,在绝大部分的情况下,第一次之后的LoadLibrary会返回第一次的HMODULE,并且增加引用计数。所以,LoadLibrary要和FreeLibrary成对出现,如果你想要释放一个dll,load了多少次,就要free多少次。
    延迟加载是当dll需要时,才会被加载。为了使用一个延迟加载的dll,我们在生成exe的时候,需要更改链接器的一些设置,表示我们要用哪些延迟加载的dll:


    此外,我们再链接想delayimp.lib,就可以延迟加载一个dll了。
    我们可以将<delayimp.h>中的__pfnDliNotifyHook2指定为我们自己的回调函数,更改默认的行为,当一个dll需要延迟加载时,将会走到这里。我们可以在这里面依据自己的需求,LoadLibrary获取一个HMODULE并返回。
2. 加载dll失败的可能原因

    当使用静态加载dll时,加载dll失败默认会得到一个错误提示框。如果使用LoadLibrary加载dll失败,那么需要用GetLastError来获取错误信息,而且错误信息可能没那么直观。以下是常见的一些失败原因。
1) 路径错误。如果你尝试LoadLibrary一个不存在的文件(它不存在于系统文件夹、PATH路径、当前exe文件夹、通过SetDllDirectory设置的文件夹)中,那么系统认为无法找到此模块。一般而言,我们都会把自己的dll放在exe同一级目录,方便查找。
2) 未能加载dll静态依赖。之前说过,dll功能和exe类似,所以它也有自己的dll依赖。我们可以用VS的dumpbin /dependents来查看某个dll依赖哪些其它dll:


可以看到,我电脑上的assimp32.dll依赖kernel32.dll,msvcr90.dll, msvcp90.dll。如果用户机器上没有其中一个dll,那么LoadLibrary返回的是NULL,并且GetLastError会得到ERROR_MOD_NOT_FOUND。
3) DllMain中返回FALSE
当加载一个dll时,如果dll有DllMain函数,它将会在加载后被调用。Windows会给这个函数一个DLL_PROCESS_ATTACH通知,表示这个dll被进程依附了。函数返回BOOL表示初始化是否成功,如果为FALSE,系统将认为初始化不成功,并且LoadLibrary将返回FALSE。
以上变是常见的加载失败的部分原因。当然还有一些原因并没有在其中说明,比如二进制不兼容等。上面的几种情况应该可以应付大部分加载场景了,希望能帮助大家理解dll的加载。

Ilingis 发表于 2023-2-15 13:33

延时加载还没用过

fwalker 发表于 2023-2-15 13:36

用dumpbin也可以查看延迟加载的dll

yukamu 发表于 2023-2-15 13:44

作者可以在以下几个方面扩展文章的内容:1. (Win98) 三类 MFC DLL 的区别和资源/状态管理(技术陈旧,仅作为 DLL 发展历史知识了解吧)。2. (WinXP?) DLL hell 和 KnownDLLs 以及 manifest + side-by-side (WinSxS) 部署方式。3. (Win8?) Microsoft/Windows API Sets

Ilingis 发表于 2023-2-15 13:45

静态导入不是CRT实现的,而是内核实现的。
页: [1]
查看完整版本: Windows下Dll的加载方式及错误处理