有一个DLL,由主(桌面)应用程序通过Windows.LoadLibrary
动态加载。这是因为有很多类似的DLL,并且在运行时仅需要加载很少或只有一个。因此,静态链接不是一种选择。
问题是,加载这些DLL之一时,主应用程序有时会挂起。请注意,问题可能很可能发生在每个人身上。可能是因为它们有很多共同的代码库。
问题似乎是加载程序锁(see this SO answer on what it is)。我在begin...end
-unit(即library
)的project.dpr
-section中找到了一段通用代码,供所有DLL在启动时使用,其中使用了GetModuleHandle
和GetProcAddress
。
我发现,这完全是与DLL无关的,因为DLL的项目文件的begin...end
-section实际上是库的DllMain
函数,调用此类函数可能会导致死锁(称为加载程序锁)。我在Microsoft Best Practice Guide中阅读了该内容。
因此,我重建了我的代码,以便在Windows.LoadLibrary
的调用完成后稍后再调用这些调用。
不幸的是,悬而未决的问题仍然存在。 :-(
然后,我运行调试器,逐步执行被调用的每一段初始化,甚至在执行我的代码的每一行之前。我确定,很多第三方代码都违反了DLL初始化代码中关于做什么和不做什么的指南:
上面所有这些,都通过
initialization
动态加载其他DLL或通过GetProcAddress
请求过程指针。我认为这些调用会在我的DLL加载时引起挂起。难道只有很少的Delphi开发人员知道
initialization
的危险吗?我该怎么办? 最佳答案
这是一个常见的问题,我认为不是特定于Delphi程序员的。如果您有在初始化部分中调用LoadLibrary
或在终结处理部分中调用FreeLibrary
的代码,则在库中使用该代码是不安全的。
请注意,我对您提到的所有库都不熟悉,也无法完全确认它们都包含initialization
节代码,这些代码在库中使用并不安全。我认为这是您需要确认的事情-我想坚持这个答案中的概念,而不是评论特定的Delphi库。
我要说的是,从GetModuleHandle
调用GetProcAddress
和DllMain
很好。我之所以这样说是因为您特别提到了GetProcAddress
。例如,通过调用GetModuleHandle
来获取模块句柄,然后通过调用GetProcAddress
来获取函数地址是绝对好的。因此,如果某些可疑库做到了这一点,并且没有调用LoadLibrary
,那么它们可能没问题。
无论如何,根据上述条件,您需要安排从DllMain
调用的,与Microsoft规定的规则相抵触的任何代码都在安全的时间而不是从DllMain
调用。不幸的是,这些规则充其量是朦胧的。微软say the following in the DllMain
documentation:
最后一段几乎没有指导。为了确信您的库将很健壮,您需要按照以下步骤进行操作:
这是我在图书馆中采用的方法,多年来一直为我服务良好。
这种方法涉及大量工作,并且不利之处在于您正在修改第三方库。但是,如果这些库在交付时无法正常工作,那么您有什么选择?
也许在较慢的时间内,您可以联系您认为与库使用不兼容的任何库的开发人员。尝试说服他们更改代码,使其与库中使用的代码兼容。从雷米对问题的评论中可以看出,图书馆开发人员很可能没有意识到这个问题,并且非常愿意进行更改。
关于delphi - Delphi DLL是否预定用于加载程序锁?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34882124/