从未设想过最初的 C 运行时库必须支持由多个模块构建的程序。它包含全局变量,如errno
和stdout
,以及具有隐式全局状态的函数,如strtok()
和malloc()
。您可以将 DLL 与其自己的 CRT 副本链接,但这对您如何设计 DLL 接口提出了相当严格的要求。您必须非常小心,不要依赖 CRT 状态。弄错这个错误几乎不可能诊断出运行时的不当行为。
解决此问题的方法是在您的流程中只拥有一份CRT 副本。这是 /MD 完成的任务,您最终将依赖于存储在 DLL 中的 CRT 版本。由所有模块共享。像msvcr120.dll,VS2013使用的那个。
编译器需要知道这一点,才能正确使用该 DLL 版本。一个简单的例子是errno
,它是一个带有 /MT 的全局变量,但它被宏编辑为带有 /MD 的函数调用,因此只有 DLL 中的一个全局变量用于跟踪最后一个已知值。如果 /MD 生效,_DLL
宏将被定义,在编译器的 .h 文件中使用。
另一个副作用是编译器会自动为 msvcrt.lib 或 libcmt.lib 插入链接指令(相当于#pragma 注释)。旨在帮助您避免错误并省略明确给出 CRT 库链接指令的需要。弄错会导致很难诊断链接器错误消息。与您尝试链接使用不匹配的 /MT 和 /MD 构建的 .obj 或 .lib 文件时得到的类型没有什么不同。哪个当然不能正常工作,你不能同时依赖两者。
.lib 都不是导入库
您按名称列出的 ole32.lib、advapi32.lib、user32.lib 实际上是导入库。它们是标准的操作系统 DLL。在运行时,您的程序将加载相应的 DLL,从调试器中很容易看到。对于 VS,您将在“输出”窗口中看到这一点。值得注意的是,这些 DLL 实际上使用的 CRT 与您的程序不同,它们绑定到 c:\windows\system32\msvcrt.dll。winapi 经过精心设计,绝不会成为问题。
链接默认使用静态链接还是动态链接
没有默认值,这取决于您链接的 .lib。区分静态链接库和导入库。构建 DLL 时会创建一个导入库。它是一个不包含代码的小文件,只有 DLL 中导出函数的名称,因此链接器知道在程序的导入表中放置一个条目。DLL依赖被解析,导入的函数在程序启动时被加载器绑定。