电子商务|LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)的缺陷

电子商务|LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)的缺陷

文章图片


如果你认真瞧过LoadLibraryEx函数的文档 , 就会发现它有这样一个标志
DONT_RESOLVE_DLL_REFERENCES 。 关于此标志 , 文档是这样描述的:
如果使用了此标志 , 并且加载的是一个DLL模块 , 系统在进程和线程初始化/退出的时候 , 不会调用DllMain , 同时 , 系统也不会加载该模块引用的其他模块 。
如果你系统仅仅是访问DLL模块中的数据或者是资源的话 , 则使用LOAD_LIBRARY_AS_DATAFILE会是一个更佳的选择 。
在我看来 , 上面所建议的LOAD_LIBRARY_AS_DATAFILE还远远不够 。
因为 ,
DONT_RESOLVE_DLL_REFERENCES这个标志实际上有点类似于一个”定时炸弹” 。
请再次认真阅读下关于这个标志的文档 , 试着深入地理解它会做什么以及它不会做什么 。
模块被加载到内存的时候 , 系统不会调用它的初始化函数 , 并且其所有依赖的模块也不会被加载 。 结果就是 , 你无法执行此模块中的任何代码 。 (说得更准确地的话 , 就是如果你试图执行该模块中的代码 , 会导致程序崩溃 , 一万DLL模块还未初始化其自身 , 并且它的DLL导入表都没有得到解析)
但是 , 和LOAD_LIBRARY_AS_DATAFILE标志不同的是 , 被加载的DLL可以被GetModuleHandle所发现 , 并且可以使用GetProcAddress 。
很明显 , 对于一个使用了标志进行加载的模块使用GetProcAddress是一个坏主意 。 因为上面我们提到过 , 你根本无法执行这个DLL中的任何代码 。 那从DLL中获取一个函数入口点又有什么意义呢?
而GetModuleHandle就会触发这个定时炸弹 。
使用GetModuleHandle来查看一个DLL是否已经加载了是一个很常见的使用场景 , 如果加载了 , 则继续使用GetProcAddress来获取一个函数的地址并调用它 。 如果DLL使用的是
DONT_RESOLVE_DLL_REFERENCES标志来加载的话 , GetModuleHandle和GetProcAddress将会调用成功 , 但是执行函数的时候会导致程序崩溃 。 执行这种操作的代码不知道DLL是使用 DONT_RESOLVE_DLL_REFERENCES 加载的 , 它没有办法保护自己 。
(请注意 , 这样做的代码无论如何都是不安全的 , 因为最初加载 DLL 的代码可能会在另一个线程上执行 FreeLibrary , 从而导致代码从第一个线程下面被撕掉 。 第二个问题可以通过使用 GetModuleHandleEx”修复”这个问题 , 可以通过指定它增加 DLL 引用计数 , 但这并不能解决第一个问题 。 )
即使你使用LoadLibrary加载DLL并将该句柄传递给GetProcAddress , 程序仍然会崩溃 , 因为LoadLibrary注意到DLL已加载并且只是增加引用计数 。
我们看看下面的例子:

如果你在没有命令行参数的情况下运行这个程序 , 那么一切都会正常运行:记事本会顺利启动 。但是 , 如果你传递一个命令行参数 , 这会启动”定时炸弹” , 并且对 ShellExecuteA 的调用会导致程序崩溃 , 因为 shell32.dll 是在没有解析其 DLL 引用的情况下加载的 。
换句话说 ,
DONT_RESOLVE_DLL_REFERENCES 从根本上说是有缺陷的 , 应该避免 。它继续存在只是为了向后兼容 。
总结【电子商务|LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)的缺陷】太长不看版:任何情况下 , 请勿使用
DONT_RESOLVE_DLL_REFERENCES , 保护他人 , 也保护自己 。
最后Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一 , 里面有很多关于Windows的小知识 , 对于广大Windows平台开发者来说 , 确实十分有帮助 。
本文来自:《LoadLibraryEx(