【问题标题】:Dynamically linked DLL is loaded immediately after starting the application启动应用程序后立即加载动态链接的 DLL
【发布时间】:2019-02-06 20:24:45
【问题描述】:

我已将 libhunspell.dll (HunSpell) 动态链接到我的应用程序。它有效,但有一个愚蠢的问题,我不知道为什么会发生。

甚至在我使用LoadLibrary("path\\to\\libhunspell.dll"); 加载并使用它之前,在应用程序启动时它会尝试自行加载库。如果我将 libhunspell.dll 放到我的主可执行文件所在的路径中,它可以加载它,否则它会在启动应用程序后立即报告错误 - 此应用程序无法启动,因为LIBHUNSPELL.DLL 未找到。重新安装应用程序可能会解决此问题。并且应用程序无法启动。

如果LoadLibrary 会使用无效路径,我会理解,但这种情况会在可执行文件运行后立即发生,甚至在WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) 中的第一条语句执行之前(我试图放置一个断点,但它甚至没有到达它,所以这发生在之前)。

因此,我必须将 libhunspell.dll 放在与我的应用程序可执行文件相同的文件夹中,而不是放在我想要的路径中。

这可能很容易解决,尽管我不知道要寻找什么。

所以问题是 - 我如何避免它立即加载它并让它等到我使用 LoadLibrary 调用?

如果有帮助的话,我是这样链接的:

1) 在 Visual Studio 2015 中编译 libhunspell.dll(我使用 /MT 选项静态链接它,因此它没有 VC++ Redistributable 作为依赖项)。

2) 使用 implib.exe -a -c -f libhunspell.lib libhunspell.dll 创建导入库 (libhunspell.lib)

3) 使用#pragma comment(lib, "libhunspell.lib") 将其链接到正在使用它的源 .cpp 单元(它是 RAD Studio 2010,因此需要 .lib 与较新的版本不同)。

4) 稍后在同一个 .cpp 中使用 LoadLibrary 加载此库并使用它。

【问题讨论】:

  • 您将不得不通过 not 使用步骤 2 和 3 来执行此操作。这将产生链接器错误,告诉您需要修复哪些代码。无疑很痛苦,所以选择简单的解决方案。
  • 显然您也在“静态”加载 DLL。这就是@HansPassant 已经说过的:您链接到导入库。如果您想动态加载 DLL,请不要这样做。
  • FWIW,对于 DLL 的“静态”加载,系统必须找到该 DLL。这意味着它必须与调用应用程序位于同一目录中,或者必须位于系统路径上(与 PATH 环境变量相同)。请参阅 Microsoft 的 Dynamic-Link Library Search Order 文档。
  • 我想我真正的问题是——有没有办法仍然使用 .lib (以避免使用有错误的GetProcAddress)但在程序的后面指定 DLL 路径(而不是在编译时也不在与主可执行文件相同的目录中或路径中的某个位置)?如果我稍后在自己的路径中使用延迟加载和LoadLibrary,它确实有效。如果@RudyVelthuis 所说的是唯一选项,那么除了修改系统PATH(或与主exe相同的路径)之外,自定义DLL加载路径的选项并不多。
  • 如果 GetProcAddress 有错误,那么很多人都会遇到大问题。无论如何,您可以使用导入库,但这不如动态链接(LoadLibrary + GetProcAddress)灵活。但是对于“静态”链接,您的 DLL 必须位于 MS 告诉您的位置之一。延迟加载不应与动态加载相结合。

标签: dll c++builder loadlibrary hunspell


【解决方案1】:

我假设您为您的 DLL添加到项目*.lib 文件。这是在应用程序初始化中完成的一种“静态”链接(在创建表单之前)。所以它有两个缺点。

  1. 您的 DLL 必须与 Apps EXE 文件位于同一路径
  2. 有时DLL文件名被锁定(无法更改)

优点是您不需要为 DLL 加载进行任何编码,因为 VCL 会为您完成...因此您的应用不应包含您刚刚包含的 LoadLibrary,GetProcAddress 调用带有正确导入声明的*.h 文件...

对于动态链接,您需要从项目中删除 *.lib 并使用 WinAPI LoadLibrary + GetProcAddress 按照 josh poley 的建议加载您的 DLL。举个例子:

请注意GetProcAddress 中存在/(是?)一个错误,在某些情况下会阻止从您的 DLL 加载所有函数。特别是如果 DLL 具有旧的名称修改,则函数的数量很高,并且 DLL 是在与相关修改不兼容的编译器上创建的。

【讨论】:

  • "您的 DLL 必须与 Apps EXE 文件位于同一路径" - 更准确地说,它需要位于操作系统的 DLL 搜索路径中的某个位置,其中包括 EXE 的文件夹作为被搜索的第一个位置。
【解决方案2】:

通过链接导入存根 (libhunspell.lib),操作系统将为您加载 DLL,因为它现在是一个静态依赖项。

一种方法是将库指定为延迟加载依赖项:/DELAYLOAD:libhunspell.lib 通过链接器选项。然后,您可以在 DLL 上调用 LoadLibrary。

唯一的其他选择是停止在链接器步骤中包含 .lib,使其真正成为动态依赖项。

【讨论】:

  • 真正的动态依赖是指使用GetProcAddress而不是导入库?
  • @Coder12345,正确,LoadLibrary + GetProcAddress 是执行此操作的标准方法(COM 是另一种方法,但可能不是您在此处寻找的方法)。
  • 如果您使用链接器的延迟加载功能,请勿手动调用LoadLibrary()GetProcAddress()。当在运行时第一次调用 DLL 函数时,延迟加载框架会自动为您调用它们。只需像您使用静态链接一样正常调用 DLL 函数。链接器将连接必要的存根以调用延迟加载。如果您想手动调用LoadLibrary()GetProcAddress(),请不要使用延迟加载功能。
  • @Coder12345 是的,使用延迟加载器时可以通过__pfnDliNotifyHook 回调自定义加载路径。当它触发dliNotePreLoadLibrary 事件时,您可以使用任何您想要的路径自己调用LoadLibrary()。或者,使用__pfnDliFailureHook 回调,当它触发dliFailLoadLibrary 事件时,如果在标准搜索路径上找不到DLL,您可以使用自定义路径调用LoadLibrary()
  • @Coder12345 说到这一点,您也可以使用SetDllDirectory()AddDllDirectory() 调整标准搜索路径以包含您的自定义路径,然后您无需手动调用LoadLibrary()全部。
猜你喜欢
  • 2023-03-03
  • 2018-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 2013-07-19
  • 1970-01-01
相关资源
最近更新 更多