从DllMain调用LoadLibrary

问题描述 投票:1回答:5

MSDN says:

它不能调用LoadLibrary或LoadLibraryEx函数(或调用这些函数的函数),因为这可能会在DLL加载顺序中创建依赖循环。这可能导致在系统执行其初始化代码之前使用DLL。

我试图从DllMain打电话给LoadLibrary但没有任何反应。

我看到的唯一问题是加载的DLL将在我的DllMain执行的其余部分之前使用我的DLL中的函数。

为什么我不能在DllMain中调用LoadLibrary?

编辑:

好吧,我意识到我不能在DllMain中调用LoadLibrary只是因为我必须像其他信徒一样相信MSDN(我在那里看到了一些错误的东西,但我也应该忘记它们)。 并且因为在较新版本的Windows中可能会发生某些事情(尽管过去十年没有任何变化)。

但是,任何人都可以显示一个代码,它将重现在DllMain中调用LoadLibrary时会发生什么的错误吗?在任何现有的Windows操作系统? 不只是在另一个内部调用一个单例初始化函数,而在DllMain中调用LoadLibrary

windows winapi language-agnostic dll loadlibrary
5个回答
12
投票

你赞成继续这样做的论点似乎是:

微软表示不这样做,但我的单个测试案例似乎有效,因此我不明白为什么没有人应该这样做。

你在一个很大的假设下运行:你假设Windows加载器的底层实现永远不会改变。如果在“Windows 8”中更改加载程序以使您的代码无法正常工作,该怎么办?现在微软因此受到指责,他们必须包含另一个兼容性黑客来解决他们告诉你不要写的代码。

遵循指南。他们并不只是为了让你的生活更加艰难,他们在那里保证你的代码在未来的Windows上和现在一样好用。


12
投票

有简单的,甚至不那么简单的情况,从DllMain调用LoadLibrary非常安全。但设计是DllMain被信任不会更改已加载模块的列表。

尽管拥有加载程序锁确实限制了DllMain中可以执行的操作,但它只与LoadLibrary规则间接相关。加载程序锁定的相关目的是序列化访问已加载模块的列表。虽然NTDLL在一个线程中在此列表上工作,但拥有加载程序锁可确保列表不会被在另一个线程中执行的NTDLL代码更改。但是,装载机锁是一个关键部分。它不会阻止同一线程重新获取加载程序锁并更改列表。

如果NTDLL在列表上工作时完全保留自己,这无关紧要。但是,NTDLL规定在此工作中涉及其他代码,如初始化新加载的DLL时。每次NTDLL在列表上工作时都会调用外部,可以选择进行设计。从广义上讲,有两种选择。一种是稳定列表并释放加载程序锁,调用外部,然后获取加载程序锁并恢复列表上的工作,就像从头开始一样,因为外部调用可能已经改变了它。另一种方法是保持加载器锁定并信任被调用的代码,不要做任何改变列表的事情。因此,LoadLibrary在DllMain中成为禁区。

并不是加载器锁做任何事情来阻止DllMain调用LoadLibrary,甚至加载器锁本身使得这样的调用不安全。相反,通过保留加载程序锁,NTDLL信任DllMain不要调用LoadLibrary。

相反,请考虑关于不等待同步对象的DllMain规则。在这里,装载机锁具有直接作用,使其不安全。等待DllMain中的同步对象设置了死锁的可能性。所需要的只是另一个线程已经拥有你正在等待的对象,然后这个另一个线程调用任何等待加载器锁定的函数(例如,LoadLibrary,还有像看似无害的GetModuleHandle这样的函数)。

想要延长或打破DllMain规则可能是恶作剧甚至是彻头彻尾的愚蠢。但是,我必须指出,对于那些质疑这些规则有多么强大或有意义的人来说,至少部分归咎于微软。毕竟,有些并不总是被清楚而有力地记录下来,而且当我最后看时,他们仍然没有记录在他们肯定需要的所有情况下。 (我想到的例外是,至少在Visual Studio 2005之前,编写DLL的MFC程序员被告知将他们的初始化代码放在CWinApp :: InitInstance中,但没有被告知此代码受DllMain规则约束。)

此外,对于任何来自微软的人来说,好像DllMain规则应该被毫无疑问地遵循,这样做会有点富裕。微软自己的程序员违反规则,并且在违反规则后继续存在导致严重的现实问题的例子。


8
投票

http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx所述:

DllMain中的线程保持加载程序锁定,因此不能动态加载或初始化其他DLL。

干杯


3
投票

我正在研究一个可能需要在DllMain中使用LoadLibrary的案例,所以在调查时发现了这个讨论。从我今天的经验来看这个更新

读这个可以得到真正可怕的http://blogs.msdn.com/b/oleglv/archive/2003/10/28/56142.aspx。不仅各种锁都很重要,而且libs传递给链接器的顺序也很重要。案件就是说一个bi

现在,我已经在win7下用vc9尝试了这个。是的,它也是。根据libs如何传递给链接器的顺序,使用LoadLibrary工作与否。但是,与win8下的vc11相同,无论链接顺序如何都能正常工作。 Application Verifier不会责怪这一点。

我现在并没有打算以这种方式使用它:)但仅仅是FYI,如果它与win10相同而且更进一步 - 这可能会有更多用处。无论如何,似乎win8下的加载器机制经历了一些明显的变化。

谢谢。


0
投票

现在已经很晚了,

如果在线程1(T1)上你DllMain加载其他库,那么将调用其他lib的DllMain;这本身就可以,但是说他们的DLLMain创建一个线程(T2)并等待一个事件让T2完成。

现在,如果T2在其处理中加载库,则加载器将无法获取锁,因为T1已经获取了它。当T2挂在LoaderLock上时,它永远不会发出T1等待事件的信号。

这将导致死锁。

可能会有更多这样的场景,我想这里的广泛推理是我们无法确定将在其他库中运行什么代码,所以这是一个好主意(转为最佳实践),不这样做。

© www.soinside.com 2019 - 2024. All rights reserved.