有一个DLL,由主(桌面)应用程序通过Windows.LoadLibrary动态加载。这是因为有很多类似的DLL,并且在运行时仅需要加载很少或只有一个。因此,静态链接不是一种选择。

问题是,加载这些DLL之一时,主应用程序有时会挂起。请注意,问题可能很可能发生在每个人身上。可能是因为它们有很多共同的代码库。

问题似乎是加载程序锁(see this SO answer on what it is)。我在begin...end -unit(即library)的project.dpr -section中找到了一段通用代码,供所有DLL在启动时使用,其中使用了GetModuleHandleGetProcAddress

我发现,这完全是与DLL无关的,因为DLL的项目文件的begin...end -section实际上是库的DllMain函数,调用此类函数可能会导致死锁(称为加载程序锁)。我在Microsoft Best Practice Guide中阅读了该内容。

因此,我重建了我的代码,以便在Windows.LoadLibrary的调用完成后稍后再调用这些调用。

不幸的是,悬而未决的问题仍然存在。 :-(

然后,我运行调试器,逐步执行被调用的每一段初始化,甚至在执行我的代码的每一行之前。我确定,很多第三方代码都违反了DLL初始化代码中关于做什么和不做什么的指南:

  • TMS组件包
  • JEDI组件库
  • OmniThreadLibrary
  • Indy组件

  • 上面所有这些,都通过initialization动态加载其他DLL或通过GetProcAddress请求过程指针。我认为这些调用会在我的DLL加载时引起挂起。

    难道只有很少的Delphi开发人员知道initialization的危险吗?我该怎么办?

    最佳答案

    这是一个常见的问题,我认为不是特定于Delphi程序员的。如果您有在初始化部分中调用LoadLibrary或在终结处理部分中调用FreeLibrary的代码,则在库中使用该代码是不安全的。

    请注意,我对您提到的所有库都不熟悉,也无法完全确认它们都包含initialization节代码,这些代码在库中使用并不安全。我认为这是您需要确认的事情-我想坚持这个答案中的概念,而不是评论特定的Delphi库。

    我要说的是,从GetModuleHandle调用GetProcAddressDllMain很好。我之所以这样说是因为您特别提到了GetProcAddress。例如,通过调用GetModuleHandle来获取模块句柄,然后通过调用GetProcAddress来获取函数地址是绝对好的。因此,如果某些可疑库做到了这一点,并且没有调用LoadLibrary,那么它们可能没问题。

    无论如何,根据上述条件,您需要安排从DllMain调用的,与Microsoft规定的规则相抵触的任何代码都在安全的时间而不是从DllMain调用。不幸的是,这些规则充其量是朦胧的。微软say the following in the DllMain documentation:



    最后一段几乎没有指导。为了确信您的库将很健壮,您需要按照以下步骤进行操作:

  • 安排要控制其源代码的任何单元的每个初始化部分都向中央注册表注册一个初始化和完成过程。
  • 在可执行项目中,注册时调用初始化过程,而在终止时以相反的顺序调用终结过程。
  • 在库项目中,您推迟了这些初始化和完成过程的调用。从DLL的使用者导出可调用的DLL中的一对函数,以请求调用这些初始化和完成过程。

  • 这是我在图书馆中采用的方法,多年来一直为我服务良好。

    这种方法涉及大量工作,并且不利之处在于您正在修改第三方库。但是,如果这些库在交付时无法正常工作,那么您有什么选择?

    也许在较慢的时间内,您可以联系您认为与库使用不兼容的任何库的开发人员。尝试说服他们更改代码,使其与库中使用的代码兼容。从雷米对问题的评论中可以看出,图书馆开发人员很可能没有意识到这个问题,并且非常愿意进行更改。

    关于delphi - Delphi DLL是否预定用于加载程序锁?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34882124/

    10-09 16:57
    查看更多