C++ 导入库是如何工作的?细节?

C++ 导入库是如何工作的?细节?,c++,c,windows,visual-c++,C++,C,Windows,Visual C++,我知道这对极客来说似乎很基本。但我想说清楚 当我想使用Win32 DLL时,通常只调用LoadLibrary()和GetProcAdderss()等API。但最近,我正在使用DirectX9进行开发,需要添加d3d9.lib、d3dx9.lib等文件 我已经听够了,LIB用于静态链接,DLL用于动态链接 所以我目前的理解是LIB包含方法的实现,并且在链接时作为最终EXE文件的一部分进行静态链接。而DLL在运行时是动态加载的,并且不是最终EXE文件的一部分 但有时,DLL文件附带了一些LIB文件,

我知道这对极客来说似乎很基本。但我想说清楚

当我想使用Win32 DLL时,通常只调用LoadLibrary()和GetProcAdderss()等API。但最近,我正在使用DirectX9进行开发,需要添加d3d9.lib、d3dx9.lib等文件

我已经听够了,LIB用于静态链接,DLL用于动态链接

所以我目前的理解是LIB包含方法的实现,并且在链接时作为最终EXE文件的一部分进行静态链接。而DLL在运行时是动态加载的,并且不是最终EXE文件的一部分

但有时,DLL文件附带了一些LIB文件,因此:

  • 这些库文件是做什么用的
  • 他们如何实现自己的目标
  • 有什么工具可以让我检查这些LIB文件的内部结构吗
更新1 查看维基百科后,我记得这些LIB文件被称为。 但我想知道它是如何与我的主应用程序和动态加载的DLL一起工作的

更新2 正如RBerteig所说,DLL附带的LIB文件中有一些存根代码。因此,调用顺序应该如下所示:

我的主应用程序-->存根在库-->实际目标DLL中

那么这些LIB中应该包含哪些信息呢?我可以想到以下几点:

  • LIB文件应包含相应DLL的完整路径;因此DLL可以由运行时加载
  • 每个DLL导出方法入口点的相对地址(或文件偏移量?)应编码在存根中;因此可以进行正确的跳转/方法调用
我说的对吗?还有别的吗


顺便问一下:是否有任何工具可以检查导入库?如果我能看到它,就不会有任何疑问。

链接到DLL文件可以在编译链接时隐式发生,也可以在运行时显式发生。无论哪种方式,DLL最终都会加载到进程内存空间中,并且其所有导出的入口点都可供应用程序使用

如果在运行时显式使用,则可以使用
LoadLibrary()
GetProcAddress()
手动加载DLL并获取指向需要调用的函数的指针

如果在生成程序时隐式链接,则程序使用的每个DLL导出的存根将从导入库链接到程序,并且这些存根将在进程启动时加载EXE和DLL时更新。(是的,我在这里简化了很多…)

这些存根需要来自某个地方,在Microsoft工具链中,它们来自一种特殊形式的.LIB文件,称为导入库。所需的.LIB通常与DLL同时生成,并且包含从DLL导出的每个函数的存根

令人困惑的是,同一库的静态版本也将作为.LIB文件提供。除了作为DLL导入库的LIB通常比匹配的静态LIB小(通常小得多)之外,没有简单的方法来区分它们

如果您使用GCC工具链,顺便说一句,您实际上不需要导入库来匹配DLL。移植到Windows的Gnu链接器版本可以直接理解DLL,并且可以动态合成大多数所需的存根

更新 如果你忍不住想知道所有的螺母和螺栓到底在哪里,到底发生了什么,那么MSDN总有一些东西可以帮助你。Matt Pietrek的文章非常全面地概述了EXE文件的格式以及如何加载和运行它。自从它最初出现在2002年的MSDN杂志上以来,它甚至被更新为包括.NET和更多内容

此外,了解如何准确了解程序使用的DLL也是很有帮助的。这方面的工具是Dependency Walker,又名Dependency.exe。Visual Studio附带了它的一个版本,但最新版本可从其作者处获得,网址为。它可以识别在链接时指定的所有DLL(早期加载和延迟加载),还可以运行程序并观察在运行时加载的任何其他DLL

更新2 我重写了一些早期的文本,以便在重读时加以澄清,并使用艺术术语的隐式和显式链接与MSDN保持一致

因此,我们有三种方法可以让程序使用库函数。接下来一个明显的问题是:“我如何选择哪种方式?”

静态链接是程序本身的大部分链接方式。将列出所有对象文件,并由链接器收集到EXE文件中。在这个过程中,链接器会处理一些小的琐事,比如修复对全局符号的引用,以便您的模块可以调用彼此的函数。库也可以静态链接。组成库的对象文件由库管理员收集在.LIB文件中,链接器在该文件中搜索包含所需符号的模块。静态链接的一个效果是,只有程序使用的库中的模块链接到它;其他模块被忽略。例如,传统的C数学库包含许多三角函数。但是,如果您链接到它并使用
cos()
,则不会得到
sin()
tan()
的代码副本,除非您还调用了这些函数。对于具有丰富功能集的大型库,这种选择性地包含模块是很重要的。在许多平台(如嵌入式系统)上,与设备中存储可执行文件的可用空间相比,库中可使用的代码的总大小可能更大。如果没有选择性的纳入,就更难管理为这些平台构建项目的细节

但是,在运行的每个程序中都有一个相同库的副本会造成负担
warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored  D.lib(JSource.obj)