Ren*_*ann 7 delphi dll dynamic-linking
有一个DLL,由主(桌面)应用程序动态加载Windows.LoadLibrary
.那是因为有很多类似的DLL,并且只需要在运行时加载很少的单个DLL.因此静态链接不是一种选择.
问题是,在加载其中一个DLL时,主应用程序每隔一段时间就会挂起.请注意,问题很可能发生在每一个问题上.可能是因为他们有很多共同的代码库.
问题似乎是装载机锁定(请参阅此SO答案).我发现了一张共同的代码,即在所使用的由所有的DLL在启动时begin...end
所述的-section library
-单元(即project.dpr
),其中GetModuleHandle
和GetProcAddress
使用.
我发现,这是一个完全没有DLL的东西,因为begin...end
DLL的项目文件的部分实际上是库的DllMain
函数,并且调用这样的函数会导致死锁(命名加载器锁).我在本微软最佳实践指南中读到了这一点.
所以我重建了我的代码,这些调用稍后在调用Windows.LoadLibrary
完成后调用.
不幸的是,悬挂问题仍然存在.:-(
然后我运行调试器,逐步完成在执行一行代码之前调用的每个初始化.我确定,许多第三方代码违反了DLL初始化代码中要做什么和不做什么的指南:
所有上述动态加载其他DLL在initialization
代码或请求过程指针通过GetProcAddress
.我认为这些调用导致我的DLL加载时挂起.
是的,只有少数Delphi开发人员知道危险initialization
吗?我该怎么办?
这是一个常见的问题,我认为这对Delphi程序员来说并不特别具体.如果您有LoadLibrary
在初始化部分或实际上FreeLibrary
在最终部分中调用的代码,那么该代码在库中使用是不安全的.
请注意,我不熟悉您提到的所有库,并且根本不确认它们都具有initialization
在库中不安全的段代码.我认为这是你需要确认的东西 - 我想坚持这个答案中的概念,而不是评论特定的Delphi库.
我会说虽然打电话给我们GetModuleHandle
并且GetProcAddress
很好DllMain
.我这么说是因为你GetProcAddress
具体提到了.通过调用GetModuleHandle
,然后通过调用获取函数地址来获取模块句柄是绝对正常的GetProcAddress
.因此,如果一些可疑图书馆这样做,并且不打电话LoadLibrary
,那么他们可能没问题.
无论如何,根据上述附带条件,您需要安排任何DllMain
违反Microsoft规定的规则的代码在安全的时间调用,而不是来自DllMain
.不幸的是,这些规则充其量是模糊的.微软在DllMain
文档中说出以下内容:
入口点功能应该只执行简单的初始化或终止任务.它不能调用LoadLibrary或LoadLibraryEx函数(或调用这些函数的函数),因为这可能会在DLL加载顺序中创建依赖循环.这可能导致在系统执行其初始化代码之前使用DLL.类似地,入口点函数在进程终止期间不得调用FreeLibrary函数(或调用FreeLibrary的函数),因为这可能导致在系统执行其终止代码后使用DLL.
因为在调用入口点函数时保证Kernel32.dll被加载到进程地址空间中,所以在Kernel32.dll中调用函数不会导致在执行初始化代码之前使用DLL.因此,入口点函数可以调用Kernel32.dll中不加载其他DLL的函数.例如,DllMain可以创建同步对象,如关键部分和互斥体,并使用TLS.不幸的是,Kernel32.dll中没有安全功能的完整列表.
最后一段给你很少的指导.为了确保您的图书馆能够强大,您需要按照以下方针做一些事情:
这是我对我的图书馆采取的方法,它已经很好地服务了我很多年.
这种方法涉及相当多的工作,并且具有修改第三方库的缺点.但是,如果这些库在交付使用时无法正常工作,您还有什么选择?
也许在较慢的时间内,您可以联系任何您认为与库中使用不兼容的库的开发人员.尝试说服他们更改代码,使其与库中的使用兼容.从Remy对您的问题的评论中可以看出,图书馆开发人员可能完全没有意识到这个问题,并且非常愿意做出改变.
归档时间: |
|
查看次数: |
725 次 |
最近记录: |