链接到 MS Visual C 上的 protobuf 3 时出错

在 Visual Studio 2013 上遇到过,但可以在任何版本中重现.

Encountered on Visual Studio 2013, but it's reproducible with any version.

我从 github 克隆了协议缓冲区库,在其上运行了 CMake-gui(我将所有内容都保留为默认值,所以它是静态版本),只构建了 libprotobuf(其他项目由于某种原因失败,cmd.exe 错误,可能有与测试有关,但 libprotobuf 构建良好).

I cloned the protocol buffer library from github, ran CMake-gui on it (I left everything to default, so it's the static version), only built libprotobuf (other project failed for some reason, cmd.exe error, might have something to do with tests, but libprotobuf builds fine).

我的项目使用在 mapbox 矢量切片规范的 github 上找到的 .proto 文件生成的标头.

My project uses headers generated with the .proto file found on the mapbox vector tiles spec's github.

当我链接时,我首先遇到这个错误

When I link, I first have this error

错误 1 ??错误 C4996:'std::_Copy_impl':带有可能不安全的参数的函数调用 - 此调用依赖于调用者来检查传递的值是否正确.要禁用此警告,请使用 -D_SCL_SECURE_NO_WARNINGS.请参阅有关如何使用 Visual C++ 'Checked Iterators' s:program files (x86)microsoft visual studio 12.0vcincludexutility 的文档

我尝试在其他命令行参数中使用 -D_SCL_SECURE_NO_WARNINGS 禁用它,但随后出现了其他错误:

I tried disabling it with -D_SCL_SECURE_NO_WARNINGS in additional command line arguments, but then I have other errors:

Error 1 error LNK2038: mismatch detection for 'RuntimeLibrary': value 'MTd_StaticDebug' 与 main.obj S:eiogit3misc-projsmaploadmaploadlibprotobufd.lib 中的值 'MDd_DynamicDebug' 不匹配(common.obj)

推荐答案

VStudio C(和 C++)运行时库 (VCRTLib 或 UCRT - 检查 libprotobuf 项目.让我详细说明:

It's a mismatch of how the VStudio C (and C++) RunTime Library (VCRTLib or UCRT - check [SO]: How to circumvent Windows Universal CRT headers dependency on vcruntime.h (@CristiFati's answer)) is used by your project and by libprotobuf project. Let me detail:

假设有一些 C (C++) 代码.该代码的目的是运行.比可以实现的:

Let's say there's some C (C++) code. The purpose of that code is to be run. Than can be achieved:

  • 直接:将该代码包含在 VC Application 类型的项目中 - 这将生成一个 .exe
  • 间接:将代码包含在 VC Library 类型的项目中 - 这将生成一个 library,它只能在从另一个 .exe 调用时运行(调用那个库).库可以是:
    • 静态:所有C(C++)代码将被编译并存储在一个.lib文件中.在另一个项目(无论是应用程序还是库)中使用该库时,您将需要该文件 - 在 link 时间.请注意,.lib 中所有需要的代码都将被复制";进入另一个项目
    • 动态:您现在将有 2 个文件:一个 .dll 文件,其中包含编译(和链接)的代码,以及一个 .libem> 文件(1) 将包含指针";(如果您愿意)到 .dll 文件中的代码.在另一个项目中使用该库时,您还需要在 link 时的 .lib 文件,但现在它不包含代码,因此不会被复制在另一个库中(另一个库会更小),但在运行时另一个库将需要 .dll 文件
    • Directly: including that code in an VC Application type project - which will generate an .exe
    • Indirectly: including the code in an VC Library type project - which will generate a library which will only be able to run when called from another .exe (that calls that library). The library can be:
      • static: all the C (C++) code will be compiled and stored in a .lib file. You will need that file when using the library in another project (whether it's an application or a library) - at link time. Note that all the needed code from your .lib will be "copied" into the other project
      • dynamic: you will have 2 files now: a .dll file which will contain the compiled (and linked) code, and a .lib file(1) which will contain "pointers" (if you will) to the code in the .dll file. When using the library in another project, you will also need the .lib file at link time, but now it won't contain the code so it won't be copied in the other library (the other library will be smaller), but at runtime the other library will need the .dll file

      您可以查看[SO]: LNK2005 Error in CLR Windows Form(@CristiFati 的回答) 有关如何将 C (C++) 代码转换为可执行格式的详细信息.此外,Google 充满了关于静态库和动态库之间差异的文章,何时使用其中一个,可以在 [SO]:何时使用动态库与静态库.

      You can check [SO]: LNK2005 Error in CLR Windows Form (@CristiFati's answer) for details of how C (C++) code gets to be transformed in executable format. Also Google is full of articles about differences between static and dynamic libraries, when to use one or the other, an example can be found on [SO]: When to use dynamic vs. static libraries.

      如您所料,CRT 或 C 运行时库(包含使 C能够运行的代码――一个例子是内存管理函数:malloc、free) 也不例外――它相当于 Nix 的 libc.a(静态或存档)与 libc.so(动态或共享对象) - 但在 VStudio 中它有点复杂:

      As you guessed, the CRT or C RunTime library (that contains the underlying system that makes C code able to run - one example are memory management functions: malloc, free) makes no exception - it's the equivalent of Nix's libc.a (static or archive) vs. libc.so (dynamic or shared object) - but in VStudio it's a little bit more complicated:

      • 静态 CRT 驻留在 libcmt.lib
      • 动态 CRT 驻留在 msvcrt.lib 中,它指向"到 msvcr###.dll(2)(msvcr120.dll 对于VStudio 2013)
      • Static CRT resides in libcmt.lib
      • Dynamic CRT resides in msvcrt.lib which "points" to msvcr###.dll(2) (msvcr120.dll for VStudio 2013)

      注意事项:

      • Ad"在库名的末尾 (msvcrd.lib),表示它是用调试符号编译的
      • C++ 运行时库是在确切的情况下;这些名称有一个额外的 p:libcpmt.lib、msvcprt.lib, msvcp120.dll
      • 有关详细信息,请查看 [MS.Docs]:CRT 库功能
      • A "d" at the end of the library name (msvcrd.lib), means that it's compiled with debug symbols
      • C++ runtime library is under the exact situation; the names have an extra p: libcpmt.lib, msvcprt.lib, msvcp120.dll
      • For more details, check [MS.Docs]: CRT Library Features

      现在,UCRT 部分不像任何其他库一样包含在项目中(项目属性 -> 链接器 -> 输入 -> 附加依赖项),但是因为它们的性质(静态或动态)在编译时是必需的,所以它们被配置为:[MS.Docs]:/MD、/MT、/LD(使用运行时库),其中有 4 个可用选项:>

      Now, UCRT parts, are not included in the project like any other lib (Project Properties -> Linker -> Input -> Additional Dependencies), but because their nature (static or dynamic) is required at compile time they are configured from: [MS.Docs]: /MD, /MT, /LD (Use Run-Time Library), where there are 4 available choices:

      1. 多线程 (/MT)
      2. 多线程调试 (/MTd)
      3. 多线程 DLL (/MD)
      4. 多线程调试 DLL (/MDd)

      显然,包含Debug"的那些是在为 Debug 配置构建而其他用于 Release 时;关键是那些有DLL的使用的是动态运行时版本,而其他的是静态版本.

      Obviously, the ones that contain "Debug" are when building for Debug configuration while the other ones for Release; the key point is that the ones that have DLL are using the dynamic runtime version, while the other ones the static version.

      回到你的错误:链接器抱怨main.obj(你项目的一部分)有MDd_DynamicDebug(链接到动态调试版本),而common.obj(libprotobuf 项目的一部分)具有MTd_StaticDebug(链接到 静态调试 版本),因此您链接到同一个可执行文件(或 .dll)中的 2 个运行时 - 这是不可能的.

      Back to your error: the linker complains that main.obj (part of your project) has MDd_DynamicDebug (linking against the dynamic debug version), while common.obj (part of libprotobuf project) has MTd_StaticDebug (linking against the static debug version), so you link against 2 runtimes in the same executable (or .dll) - which is not possible.

      为了修复它,您应该确保 libprotobuf 和您的主项目对于 UCRT 具有相同的值.
      当然,更改更简单您的主要项目设置与 libprotobuf 的设置相匹配,但建议使用动态运行时版本(在涉及 .dll 的大型项目中,事情可能会变得混乱)甚至如果这需要重新编译 libprotobuf(好吧,如果更改该选项会产生错误,使 libprotobuf 很难构建,并且您的项目将保持如此简单,您可以使用静态UCRT).

      In order to fix it, you should make sure that both libprotobuf and your main project have the same value for UCRT.
      Of course it's simpler to change your main project setting to match libprotobuf's one, but it's recommended to use the dynamic runtime version (things can get messy in larger projects that have .dlls involved) even if this requires to recompile libprotobuf (well, if changing that option generates errors that make libprotobuf very hard to build, and your project is going to stay this simple, you can use the static UCRT).

      注意:不要将 UCRT 类型(静态/动态)误认为 libprotobuf 的构建方式(此时是静态的,但是我相信它也可以构建为动态的).

      Note: Not to mistake UCRT type (static / dynamic) with the way libprotobuf is being built (static at this point, but I'm sure that it can be built as dynamic too).

      在上面的注释中添加一些额外的信息,因为一些评论要求它,它可能对其他用户有用.

      Adding some additional info on the above note, as some comments requested it, and it might be useful to other users.

      关于库(包括libprotobuf)有两个方面完全不相关:

      There are 2 aspects about a library (including libprotobuf), that are totally unrelated:

      1. 库类型(构建方式):动态/静态
      2. UCRT 类型(它使用 UCRT 的方式):同样,动态/静态
      1. Library type (the way it is being built): dynamic / static
      2. UCRT type (the way it uses UCRT): again, dynamic / static

      因此,有 4 种完全有效的组合:

      So, there are 4 perfectly valid combinations:

      1. 使用动态UCRT
      2. 的动态库
      3. 使用静态UCRT
      4. 的动态库
      5. 使用动态UCRT
      6. 的静态库
      7. 使用静态UCRT
      8. 的静态库

      对于 libprotobuf,每个方面都由一个布尔 cmake 选项控制:

      For libprotobuf, each of the aspects is controlled by a boolean cmake option:

      1. 库类型:protobuf_BUILD_SHARED_LIBS
      2. UCRT 类型:protobuf_MSVC_STATIC_RUNTIME

      可以通过以下任一方式设置 2 个标志:

      The 2 flags can be set by either:

      • cmake-gui
      • cmake cmdline(将它们作为参数传递 - 例如:-Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF)
      • cmake-gui
      • cmake cmdline (passing them as arguments - e.g.: -Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF)

      上述 4 种组合因此可能(至少在 v3.5 中),但是#2. 默认禁用(指定 -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON 将构建一个 .dll动态 UCRT),以避免可能的运行时问题,启用它需要手动干预.

      The above 4 combinations are thus possible (at least in v3.5), but #2. is disabled by default (specifying -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON will build a .dll which will link to the dynamic UCRT), in order to avoid possible runtime problems, and enabling it requires manual intervention.

      有关构建说明的更多详细信息(通过 cmake),请查看:[GitHub]: protocolbuffers/protobuf - (master) protobuf/cmake/README.md.

      For more details regarding build instructions (via cmake), check: [GitHub]: protocolbuffers/protobuf - (master) protobuf/cmake/README.md.

      • #1:只有在库导出符号时才会创建.lib文件, 因为否则它没有意义(链接时不需要任何东西,并且 .dll 将被创建,但几乎无法使用)

      • #1: The .lib file will only be created if the library exports symbols, as it wouldn't make sense otherwise (nothing needed at link time, and the .dll will be created, but pretty much unusable)

      #2:对于较新的 VStudio 版本(从 v2015 开始em>),msvcr(t) 部分已被替换为 vcruntime(或者至少这是入口点,因为它被分成更小的逻辑部分(检查开头的 URL))

相关文章