EXE和DLL加载共享DLL导致无效指针操作并在终止时挂起

时间:2015-03-12 00:57:21

标签: delphi dll loadlibrary

我有一个Executable项目,该项目使用另一个DLL项目并使用LoadLibraryGetProcAddress动态加载它。另一方面,我还有另一个第二个DLL,它由EXE和第一个DLL使用,也使用LoadLibraryGetProcAddress。我有一个包含共享DLL的公共单元,并在EXE和第一个DLL中使用。

问题是,在EXE加载了第二个DLL及其过程地址后,然后EXE加载了第一个DLL,然后第一个DLL再次加载第二个DLL,因此它被加载两次。最终结果是,当从EXE卸载第一个DLL时,它还卸载第二个DLL - 它实际上共享同一个实例 - 因此也卸载了原始EXE已知的实例。这导致关闭时出现Invalid Pointer Operation错误,因为第二个DLL已经卸载(并且它正在尝试再次卸载它)以及挂在后台的EXE不可见(使用一个完整的处理器核心)。

如果只有EXE终止,我如何确保不卸载第二个(共享)DLL?

我可以通过两种方式来适应这种情况。

解决方案1 ​​

在加载时将HMODULE实例和每个proc地址传递给第一个DLL,并使用该实例而不是尝试再次加载它。但是,这个DLL有大量的导出函数需要传递到每个实例中。

解决方案2

向第二个DLL添加条件,如IS_DLL,如果定义了此条件,则不要编译释放共享库。 可以安全地拨打LoadLibrary两次 - 第二次应该重复使用第一次。

{$IFNDEF IS_DLL}
FreeLibrary(FModule);
{$ENDIF}

我不确定第二种解决方案对我的假设是否准确,但这将是我的首选解决方案,因为第一种解决方案将会有更多工作(考虑到DLL的大小和复杂性)。我已经尝试了它并且它有效,我只是不确定可能存在哪些问题。

1 个答案:

答案 0 :(得分:2)

  

如果只有EXE终止,我如何确保不卸载第二个(共享)DLL?

DLL由内部引用计数管理。每当您致电LoadLibrary时,参考计数都会增加。第一次调用LoadLibrary加载库并将引用计数设置为1。后续调用会增加引用计数并返回相同的模块句柄。调用FreeLibrary会减少引用计数。当它变为零时,库被卸载。每次对LoadLibrary的调用都应与另一位FreeLibrary匹配。

所有这些文件都很清楚。来自LoadLibrary

  

系统维护所有已加载模块的每进程引用计数。调用LoadLibrary会增加引用计数。调用FreeLibrary或FreeLibraryAndExitThread函数会减少引用计数。系统在其引用计数达到零或进程终止时卸载模块(无论引用计数如何)。

来自FreeLibrary

  

释放加载的动态链接库(DLL)模块,并在必要时减少其引用计数。当引用计数达到零时,模块将从调用进程的地址空间中卸载,并且句柄不再有效。

您可以从所有这些中得出结论,您错误地诊断了您的问题。你们两个提出的“解决方案”都不需要。那是因为无论你的问题是什么,都不是你想象的那样。您只需要调用LoadLibrary并依赖系统来保持模块加载,只要引用计数为正。

  • 如果DLL正在卸载,那就是因为你调用了FreeLibrary并且它的引用计数变为零。如果您希望它保持加载状态,请在完成之前不要致电FreeLibrary
  • 如果DLL没有卸载,那么你的问题显然不是你说的那样。