【问题标题】:DLL reference vs DLL Implicit LinkingDLL 引用与 DLL 隐式链接
【发布时间】:2021-11-02 22:11:30
【问题描述】:

我最近才了解如何通过隐式链接或显式链接将可执行文件链接到 DLL;但是,它让我对项目(或 DLL)引用感到困惑。

当您可以将隐式链接添加为 Visual Studio 中的引用时,为什么还要使用它?隐式链接需要您通过在头文件中标记 __declspec(dllimport) 来导出函数以便使用它。另一方面,如果您添加一个项目库作为引用,您可以只使用 #include "header.h" 并使用类似于 DLL 中的函数。拥有this feature 有什么意义?

【问题讨论】:

  • “是否存在性能差异?” 检查和编组反射变量和函数的成本可能是多少?它并非没有。
  • 你说的是非托管 c++ 还是 c++-cli BTW?
  • @πάνταῥεῖ 哦,哪一个是托管的?哪一个是不受管理的?我认为它们对两者都有效。
  • 好的,你先决定使用哪种语言怎么样?
  • @Charlieface 我猜它应该是非托管 C++。所以隐式和显式链接不适用于托管 C++?

标签: c++ dll reference


【解决方案1】:

当您可以将隐式链接添加为 Visual 中的引用时,为什么还要使用它 工作室?

据我所知,当您引用它时,Visual Studio 会在后台进行隐式链接。我认为您对这两种方式有些困惑。让我单独清楚地解释一下:

隐式链接:

在这里,您有一组翻译单元,它们被编译到一个共享库中(Windows 上的.dll)。并且随之生成了一个.lib 文件。 .lib 文件包含函数指针声明,这些函数的定义位于 .dll 中。当您链接该.lib 文件时,应用程序将自动查看其路径中的.dll。如果它发现,它将加载它。加载 .dll 后,您可以使用该 .dll 中的任何函数,就像您与 .lib 链接一样。请注意,标记为“__declspec(dllexport)”的函数将被“导出”。

那么隐式链接有什么用处?

  • 多个应用程序可以加载一个.dll,如果您有相同的代码在应用程序之间共享,您可以将其添加到.dll
  • 它使.dll 的使用变得更容易,但这是有代价的,与显式链接不同,您不能“重新加载”它。

显式链接:

在这种情况下,即使您生成.lib,您也不会链接到它。相反,您使用 Win32API(或您的操作系统的 API)来加载共享库。在 Windows 上,您使用 LoadLibrary("Path/To/The/DLLFile.dll")。这给了你一个HMODULE 对象。然后使用函数GetProcAdress(sHMODULE, "Function Name") 手动检索函数指针。最后,调用FreeLibrary(sHMODULE). 卸载库。请注意,这发生在运行时。在这种情况下,您也必须用“__declspec(dllexport)”标记您的函数。

那么显式链接有什么用处?

  • 在支持 C++ 脚本的游戏引擎(如 Unreal)中,它们使用显式链接来热重载脚本代码。
  • 如果您想在应用程序中使用插件系统

【讨论】:

  • 另一种方法是使用/DELAYLOAD 然后__HrLoadAllImportsForDll()GetModuleHandle()
  • 句柄在GetProcAddress() 中使用,以防没有.lib
【解决方案2】:

您在link 中发布的关于隐式链接的重要段落是:

[...] 操作系统在加载 DLL 的同时加载使用它的可执行文件。

所以您的dll 必须在场才能启动应用程序,无论您是否使用它。假设您有一个使用 dlls 作为其插件系统的可扩展应用程序,您需要事先知道将会有哪些插件并且需要它们的所有库。

或者,您只需明确检查任何插件 dlls 并在运行时导入它们。

更多显式链接示例:

  • 根据当前系统选择实施
  • 根据用户输入选择实施
  • 如果预期的 dll 不存在时的故障安全机制(无 DirectX,使用 OpenGL)
  • ...

还可以查看this thread about implicit vs explicit linking

关于术语的一些说明:

  • “#include”,包括由编译器在编译时发生,不需要完全定义的类和函数。这意味着如果您缺少库,您的编译器将不会抛出错误
  • 链接发生在编译后,由链接器完成。在这里,链接器会检查您的所有函数和类(符号)是否都已定义。
  • 加载库(隐式或显式)发生在运行时。上面已经提到了这方面的细节。

【讨论】:

  • 我不明白的是,当您可以使用#include 指令时,为什么还要麻烦使用隐式链接。为了使用隐式链接,DLL 导出的类、函数和数据都必须在头文件中标记为 __declspec(dllimport)。如果我添加 DLL 作为对可执行项目的引用,我可以添加 #include "header.h" 并使用这样的函数。在这两种情况下,操作系统都会在加载 DLL 的同时加载使用它的可执行文件。什么时候需要使用隐式链接?
  • @IceDrake '必须全部标记' 是完全错误的。这个东西是不需要的。与.lib 链接后,您拥有所有符号。你可以用__declspec(dllimport) haha​​ 放弃这个案例
  • @IceDrake 你混淆了很多概念和术语。通过#includeing 一个头文件,您只是向编译器显示存在一些结构和函数;在这个阶段,它们是否被定义并不重要。通过指定如何使用库,您可以告诉链接器如何以及使用哪些库。
  • @Baumflaum 混淆开始于“DLL 导出的类、函数和数据都必须在头文件中标记为 __declspec(dllimport)”这一事实。这让我认为添加 DLL 作为引用不同于隐式链接。在观看 Youtube 视频完整解释如何使用动态库后,虽然结合 АлексейНеудачин 说不需要 __declspec(dllimport),我发现它们是一回事。混合托管和非托管 C++ 让我更加困惑。哈哈哈。
  • 请用缺少的说明更新您的答案,以便我可以接受它作为解决方案,如果其他人像我一样感到困惑,也可以从中受益。我会奖励你的赏金。
【解决方案3】:

你有点糊涂了。

  • 隐式链接:需要 .lib 文件,其中指定了所有导出的符号。这是将 lib 和 dll 文件放入搜索路径(工作目录或您必须在链接器选项期间指定的某些自定义路径)中的常用方法。当可执行文件调用一个函数或引用该dll中的任何符号时,它将加载该dll。

  • 显式链接:在某些情况下,您必须在运行时加载 dll。在这种情况下,可执行文件需要使用 loadlibrary 或类似的 API 从特定路径加载库。

在你的情况下,你应该选择第一个选项。

【讨论】:

    猜你喜欢
    • 2011-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    • 2015-12-05
    • 1970-01-01
    相关资源
    最近更新 更多