【问题标题】:Is it safe to mix delayed dll loading and manual call of LoadLibraryA?混合延迟加载 dll 和手动调用 LoadLibraryA 是否安全?
【发布时间】:2019-10-12 13:33:47
【问题描述】:

我正在为 Windows 编译一个程序。

我希望它检查系统中是否存在 foo.dll,如果不存在,则打印错误消息并退出。这样做是否安全:

  1. /DELAYLOAD foo.dll 标志传递给链接器;
  2. 在main()的最开始,手动调用auto handle = LoadLibraryA("foo.dll"),检查handle是否不为NULL;
  3. 如果不为NULL,继续工作;
  4. 在 main() 结束时,调用 FreeLibrary(handle)?

我想知道是否会由于混合延迟加载和手动调用 LoadLibraryA() 而导致中断。另外,如果有人可以提出一种更简单或更正确的方法来做我想做的事,我将不胜感激。

【问题讨论】:

  • 真的可以在解决延迟加载 api(s) 的同时处理错误。如果您使用标准 __delayLoadHelper2(来自 delayimp.lib) - 您可以实现 __pfnDliFailureHook2 或处理 VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) 异常。但是如果你想停止工作,如果 foo.dll 没有找到 - 为什么不正常链接到它,没有延迟加载?您的程序根本无法启动并显示错误 - foo.dll 未找到。如果您想要工作,即使 foo.dll 不存在(或不导出所有 api),使用 delayload 也是有意义的,但是您以某种方式处理这种情况
  • 如果您只需要检查 DLL 的存在和版本信息,请致电 LoadLibraryExW(L"foo.dll", NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE)。如果它已经被映射为一个模块,它会增加引用计数并返回对它的引用。否则,它将 DLL 映射为数据段。
  • @RbMm,延迟加载可以让程序有机会通过SetDefaultDllDirectoriesAddDllDirectory 修改默认加载程序搜索路径,通过省略PATH 和DLL 中的工作目录来提高安全性搜索并让应用程序完全控制在哪里查找其依赖项。 SxS 激活上下文可以实现相同的目标甚至更多,但配置起来要复杂得多。
  • @ErykSun 是的,如果某些 dll 很少,并不总是在正常执行路径中使用,我们可以只在真正需要时才加载它,但不是无条件地加载它。或者,如果某些 api 仅存在于最新版本的 Windows 中,则可以通过延迟加载等方式处理此问题,但如果只想在开始时停止运行应用程序,如果找不到 dll - 更合乎逻辑的已经使用直接导入而不会延迟。当然如果不想做一些工作以防 dll 不存在
  • @RbMm 也许 OP 想要有条件代码:如果可以加载 DLL,则执行一个块,如果无法加载则运行一个块。我相信,您的“以某种方式处理此案”正是 OP 试图建立的。

标签: c++ windows dll linker delayed-execution


【解决方案1】:

如果您要这样做,您应该在“测试”呼叫LoadLibrary() 之后立即呼叫FreeLibrary() 立即 - 但肯定是在您通过调用其中一个导致自动加载之前套路! (当然,假设调用成功!)这样,应该不会出现负载冲突问题。 (您还可以通过调用GetProcAddress() 进行进一步测试,例如检查所有必需的例程是否存在。

一旦您确认 DLL 存在(并且显然是可加载的),您就可以继续执行 - 当您的可执行文件第一次调用其导出的函数之一时,DLL 将“自动”加载。

如果您已经(手动)加载了 DLL,那么它会尝试将自身两次加载到同一个进程中(我认为)。这肯定会在某些时候引起问题。

PS:这是检查的好方法,恕我直言!请告诉我们它的票价。

编辑:在 cmets 中进行了(有些广泛的)讨论之后,我现在很清楚没有必要致电 FreeLibrary() - 无论是在我建议时,还是在程序结束时(因为无论如何它都会被卸载)。但这仍然是一个很好的解决方案!

【讨论】:

  • 您应该在“测试”调用后立即调用 FreeLibrary() - 为什么?更多的逻辑真的已经根本不叫FreeLibrary了。 如果您已经(手动)加载了 DLL,那么它会尝试将自己两次加载到同一个进程中 - 这当然是错误的
  • @RbMm 在“测试”成功后调用FreeLibrary() 有什么问题?也许,正如 Eryk 所说,如果你不这样做,它不会加载两次,但是在你的探测操作之后“清理”不是一个好习惯吗? (注意:我确实在帖子中写过我认为!)
  • @Adrian call FreeLibrary 没有错(自己)但是如果我们无论如何计划使用这个库 - 卸载它然后再次加载?更合乎逻辑的不卸载它已经
  • @Adrian - 你错了。不需要任何再次加载。 foo.dll 将被加载一次。在我们在 main 开始加载它之后 - 不需要卸载/重新加载它。你混淆了加载 dll 并从它解析导入
  • @Adrian - 存在 2 个不同的东西 - 将 dll 加载到内存(第一步)并从这个 dll 解析导入函数。我最初的评论是关于 - 如果我们已经将 foo.dll 加载到内存中 - 没有意义卸载它并再次加载。只有这个。然而,即使存在 foo.dll 也可能是一些需要的 api,它没有导出(旧版本?另一个 dll)这里可能的解决方案不仅仅是加载 dll,而是在开始时也解决所有从它的导入。在 win 8+ 中我们可以在这里使用ResolveDelayLoadsFromDll api。
猜你喜欢
  • 1970-01-01
  • 2021-05-06
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 2017-02-11
  • 1970-01-01
  • 2013-11-28
相关资源
最近更新 更多