CRT 库类型

CRT library type

我试图更好地掌握 Visual Studio 2013 中的 CRT 库选项(C++ -> 代码生成 -> 运行时库)以及如何知道 select 的哪个选项(以及何时更改默认值)。

来自MSDN

A reusable library and all of its users should use the same CRT library types and therefore the same compiler switch.

因此,我的理解是,如果您要链接第三方库,则应使用用于构建库的相同 CRT 版本。构建库的人应该指定构建中使用的 CRT 选项。

是否可以通过查看 .lib 文件来确定使用的是哪个 CRT 版本?

更重要的是,如果您不链接任何第三方库,您将如何决定使用哪个选项?您什么时候会考虑更改默认值?

简答:

So, my understanding is that if you are linking with a third party library, you should use the same CRT version that was used to build the library. Whoever built the library should specify what CRT option was used in the build.

这是最不容易出错的选项。混合 运行 次是可能的,但如果这样做,您可能会 运行 出现意想不到的错误。

Is there a way to determine which CRT version was used just by looking at the .lib file?

我不知道 .lib 本身,但如果第三方代码有 DLL 或 EXE,您可以使用 [=273 查看它依赖于哪些 CRT DLL =] Dependency Walker 工具.

如果它是静态库,并且您的代码的 CRT 选择不匹配,您将在构建时看到警告。

More importantly, how would you decide which option to use if you aren't linking with any third-party libraries? When would you consider changing the default?

对于尽可能简单的部署,静态 linking 是最好的;您可以单独发送可执行文件,它将 运行。对于具有多个 EXE 和 DLL 的大型项目,如果您使用静态-link,您的代码量会更大。如果您在同一个进程中有多个模块(EXE 加上 1 个或多个您自己的 DLL),最好共享相同的 CRT 代码。

更多细节(基于我之前写的博客post):


库变体

C/C++ 运行time 库有四种变体,您可以针对它们构建代码:

  • 多线程调试 DLL
  • 多线程 DLL
  • 多线程调试
  • 多线程

您可以 select 通过在 Visual Studio 中右键单击您的项目和 selecting 属性,单击 代码生成选项在弹出的对话框中C/C++下,进入Runtime Library属性.

请记住,此设置是针对每个配置的,因为您需要为 Debug[=] 选择 Debug 运行time 库186=] 配置,Release 运行时间库用于 Release 配置。

有什么区别?

DLL运行时间库选项意味着您link动态地针对C/C++运行时间,对于您的程序 运行,DLL 需要位于您的程序可以找到它的地方(稍后会详细介绍)。

没有提到DLL的选项(多线程调试多线程发布)导致你的程序静态-link 反对 运行 时间。这意味着你的程序不需要外部DLL 运行,但是你的程序会因为额外的代码而变得更大,还有其他原因你可能不想选择它。

默认情况下,当您在 Visual Studio 中创建新项目时,它将使用 DLL 运行time.

运行时代名称中的多线程是过去同时存在非线程安全和多线程C/C++ 运行时代的遗产.即使您自己的应用程序是单线程的,您也将始终使用 多线程 运行时间和现代 Visual Studio。

部署 DLL 运行时

如果您 link 针对 DLL 运行 次,那么您将不得不考虑在发布程序(测试和客户)时如何部署它们.

如果您向测试团队交付 Debug 版本,请确保您也提供 运行 次 DLL 的 Debug 变体。

此外,不要忘记获得正确的架构(例如 x86x64)。

可再发行安装程序

Microsoft 提供安装 Release(但不是 Debug)DLL 的可再发行组件包。通过搜索 Visual C++ Redistributable 2013(用 Visual Studio 版本替换您需要的版本),可以很容易地找到这些文件。也可以直接去微软官网Latest Supported Visual C++ Downloads

这些可再发行组件包是可执行文件,您可以从程序的安装程序中调用它们(或者您可以 运行 手动设置测试环境)。请注意,x86 和 x64 有单独的可再发行组件。

合并模块

如果您正在构建 MSI 安装程序,您可以将 C/C++ 运行 时间的 Merge Module 合并到安装程序包中。这些合并模块通常位于 C:\Program Files (x86)\Common Files\Merge Modules 中。例如,x86 C/C++ 运行time 的 Visual Studio 2013 合并模块称为 Microsoft_VC120_CRT_x86.msm.

请注意,这些合并模块名称中的版本号不是基于年份的产品版本,而是内部版本号。 This table on Wikipedia 显示版本号之间的映射以避免混淆。

与上面的独立可执行可再发行安装程序不同,合并模块也有调试变体。

某些版本的 Visual Studio 支持 Visual Studio 安装程序 项目类型(在 安装和部署 类别在 其他项目类型 ) 中,如果您将程序项目的输出包含在其中一个安装程序中,则 运行 时间的合并模块将自动包含在内。您还可以使用它作为获得安装程序的技巧,该安装程序将安装 Debug 运行 次用于内部测试目的(任何虚拟 C/C++ 项目都可以,它不必实际安装您的程序)。

从 Redist 文件夹复制

您也可以只将 DLL 从 Visual C++ 安装中的 redist 文件夹复制到安装程序的位置。例如,对于 Visual Studio 2013,您会在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\x64\Microsoft.VC120.CRT\.

等地方找到 x64 C/C++ 运行time 库

可以在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\Debug_NonRedist.

等地方找到调试变体

(根据您的 Visual Studio 版本和安装位置调整路径)。

混合 C/C++ 运行时

在理想情况下,您将使用相同的 C/C++ 运行time 库变体(Debug vs ReleaseDLL vs statically-linked),并且都来自相同版本的 Visual Studio,对于所有被 link 编辑到您的程序中的库。

如果你混合 运行 次,你可能会得到 linker 错误,你的程序可能根本无法 运行,或者更糟的是,它可能看起来是工作但仅在某些情况下崩溃或给出错误结果。

忽略默认库

一个常见的情况是你只有一些第三方库的 Release 版本,但你仍然希望能够构建 Debug 使用此库的您自己的代码变体。另一种情况是,您有一个使用静态 C/C++ 运行time 的库,并且您希望在程序中使用 DLL 版本,或者相反。

如果第三方库是 C 代码,那么使用 /NODEFAULTLIB linker option.

您可能能够摆脱它并使其实际工作

如果库是 C++ 代码,您可能就不走运了,因为在特定的 运行 时间内,太多生成的代码与符号相关联。

这些是不同的库名称:

  • LIBCMT.LIB:静态linked发布运行时间(a.k.a。多螺纹)
  • LIBCMTD.LIB:静态linked调试运行时间(a.k.a。多线程调试)
  • MSVCRT.LIB:动态-linked 发布运行时间(a.k.a。多-螺纹 DLL)
  • MSVCRTD.LIB: 动态-linked 调试 运行time (a.k.a. Multi-线程调试 DLL)

请记住,您要忽略的 运行time 库是 third-party 代码正在使用的库,即它与你自己的程序正在使用。如果您查看构建输出,系统会提示您选择正确的,例如:

LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library

您可以通过右键单击项目来指定要忽略的库,selecting Properties,单击 Input Linker 下的条目,并将 运行time 库名称添加到条目中。

跨越模块边界

您还可以 运行 进入 C/C++ 运行 时间不匹配问题,以更微妙的方式跨越模块边界(例如,在 EXE 和它加载的 DLL 之间)。

例如,C库中的数据结构可能被不同的运行次定义。我曾在使用 API 中使用 FILE* 的 DLL 的程序中看到此原因导致崩溃。一个文件在一个模块中使用一个 C 运行 时间打开,并与另一个具有不同的、不兼容的实现的模块交互。更安全的选择包括为这样的事情传递 Windows API HANDLE 对象,或者以 运行 时间不可知的方式包装文件交互。

不同运行次也使用自己的内存堆进行分配。如果一个对象在一个堆上的一个模块中分配,但在另一个模块中释放,如果 C/C++ 运行 时间不匹配,则可能会发生崩溃。这当然只适用于使用 C 或 C++ 运行 时间的分配,例如 mallocnew。例如,如果到处都在使用默认 Windows 进程堆,则一切都很好。

请注意,如果模块静态 link 针对 C/C++ 运行 时间,即使它是 [=285= 的相同变体,它们也会出现此问题]他们 link 反对的时间,因为仍然会有两个不同的 运行 时间,他们自己的内存堆在发挥作用。因此,在这种情况下,您会希望使用 DLL C/C++ 运行time。

如果 运行time-implemented 功能如异常或 类 与 vtables 交叉,则在每个模块中使用(相同版本的)DLL 运行times 也是明智的模块边界,尽管某些不匹配的组合在实践中可能会起作用。