在MS Visual C上链接protobuf 3时出现错误

5

在Visual Studio 2013上遇到了问题,但任何版本都可能会出现这个问题。

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

我的项目使用在mapbox vector tiles规范的github上找到的.proto文件生成的标头。

当我进行链接时,首先出现了这个错误:

Error 1 error C4996: 'std::_Copy_impl':函数调用具有可能不安全的参数 - 此调用依赖于调用者检查传递的值是否正确。要禁用此警告,请使用-D_SCL_SECURE_NO_WARNINGS。请参阅有关如何使用Visual C++“Checked Iterators”的文档 s:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility

我尝试使用-D_SCL_SECURE_NO_WARNINGS在附加命令行参数中禁用它,但然后我有其他错误:

Error 1 error LNK2038: 检测到“RuntimeLibrary”的不匹配:值“MTd_StaticDebug”与main.obj中的值“MDd_DynamicDebug”不匹配 S:\eiogit3\misc-projs\mapload\mapload\libprotobufd.lib(common.obj)


所以,您使用CMake构建了libprotobuf的静态版本(_.lib_),现在您正在尝试将其链接到您的VStudio应用程序中? - CristiFati
是的,我用 MSVC 2013 构建了它,现在正在使用同样的 MSVC 2013 将其链接到我的项目中。 - jokoon
这里是项目文件:https://github.com/jokoon/eio/tree/master/misc-projs/mapload - jokoon
1个回答

14
这是一个如何解决 VStudio C (和 C++) 运行时库 (VCRTLibUCRT - 请参阅 [SO]: 如何规避 Windows 通用 CRT 标头对 vcruntime.h 的依赖 (@CristiFati's answer)) 在您的项目和 LibProtoBuf 项目中使用不匹配的问题。让我详细说明一下:
假设有一些 C (C++) 代码,其目的是要运行。可以通过以下方式实现:
直接:将该代码包含在一个VC应用程序类型的项目中 - 这将生成一个.exe文件。
间接:将代码包含在VC库类型的项目中 - 这将生成一个库,只能在被另一个.exe文件(调用该库的文件)调用时运行。该库可以是:
静态的:所有的C(C++)代码将被编译并存储在一个.lib文件中。在使用该库时(无论是应用程序还是库),需要在链接时使用该文件。请注意,你的.lib文件中的所有所需代码都将被“复制”到其他项目中。
动态的:现在你将有两个文件:一个.dll文件,其中包含已编译(和链接)的代码,以及一个.lib文件(1),其中包含指向.dll文件中代码的“指针”。在另一个项目中使用该库时,你还需要在链接时使用.lib文件,但现在它不会包含代码,因此不会将其复制到其他库中(其他库将更小),但在运行时,其他库将需要.dll文件。
您可以查看[SO]: LNK2005错误在CLR Windows表单中(@CristiFati的答案),了解有关如何将C (C++)代码转换为可执行格式的详细信息。此外,Google上有很多关于静态库和动态库之间差异以及何时使用它们的文章,例如[SO]:何时使用动态库与静态库的示例
正如您猜测的那样,CRTC运行时库(其中包含使C代码能够运行的底层系统 - 一个例子是内存管理函数:mallocfree)也不例外 - 它相当于Nixlibc.a(静态或档案)与libc.so(动态或共享对象)-但在VStudio中要复杂一些:
  • 静态的 CRT 位于 libcmt.lib 中。

  • 动态的 CRT 位于 msvcrt.lib 中,该库“指向” msvcr###.dll(2)(对于 VStudio 2013,是 msvcr120.dll)。

注:

  • 库名以 "d" 结尾 (msvcrd.lib) 表示它是带有调试符号的构建版本(也更大、更慢)

  • C++ 运行库同样处于这种情况;它们的名称多了一个 plibcpmt.libmsvcprt.libmsvcp120.dll

  • 更多细节请参见 [MS.Docs]: CRT Library Features

现在,UCRT部件不像其他库一样被包含在项目中(项目属性 -> 链接器 -> 输入 -> 附加依赖项),但由于它们的性质(静态或动态)需要在编译时配置,因此它们从[MS.Docs]: /MD、/MT、/LD(使用运行时库)配置,有4个可用选项:
  1. 多线程(/MT

  2. 多线程调试(/MTd

  3. 多线程 DLL(/MD

  4. 多线程调试 DLL(/MDd

显然,包含“Debug”的是在构建Debug配置时使用的,而其他的则是针对Release的;关键点是那些具有DLL的是使用动态运行时版本,而另一些则是使用静态版本。

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

为了解决这个问题,您应该确保LibProtoBuf和您的主项目对于UCRT拥有相同的值。
当然,更简单的方法是将您的主项目设置更改为与LibProtoBuf相匹配的设置,但即使需要重新编译LibProtoBuf(如果更改该选项会生成使LibProtoBuf非常难以构建的错误,并且您的项目将保持这么简单),也建议使用动态运行时版本(在涉及到.dll的大型项目中可能会变得混乱)。
注意:不要将UCRT类型(静态/动态)与LibProtoBuf正在构建的方式(目前为静态,但我确定它也可以构建为动态)混淆。

更新 #0

补充一些有关上述注释的额外信息,因为一些评论要求提供这些信息,而且对其他用户可能也有用。

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

  1. 库类型(构建方式):动态 / 静态

  2. UCRT类型(使用UCRT的方式):同样是动态 / 静态

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

  1. 使用动态UCRT 的动态库

  2. 使用静态UCRT 的动态库

  3. 使用动态UCRT 的静态库

  4. 使用静态UCRT 的静态库

对于LibProtoBuf,每个方面都由一个布尔型的CMake选项控制:
  1. 库类型:protobuf_BUILD_SHARED_LIBS
  2. UCRT类型:protobuf_MSVC_STATIC_RUNTIME
这2个标志可以通过以下方式之一设置:
  • CMake-GUI

  • CMake CmdLine(将它们作为参数传递,例如:-Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF

以上4种组合因此在v3.5中是可能的,但是第2种组合默认被禁用(指定-Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON将构建一个链接到动态UCRT的.dll),以避免可能的运行时问题,启用它需要手动干预。
关于构建说明的更多详细信息(通过cmake),请查看:[GitHub]:protocolbuffers/protobuf - (master) protobuf/cmake/README.md

脚注

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

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


这个有没有解决问题呢?https://github.com/google/protobuf/tree/master/cmake - jokoon
谢谢,现在它已经构建成功了(使用命令行中的-D_SCL_SECURE_NO_WARNINGS)。我已经知道静态和动态之间的区别,但我对运行时库设置一无所知。实际上,Google Protobuf文档指出建议使用静态版本而不是动态版本:https://github.com/google/protobuf/tree/master/cmake#dlls-vs-static-linking - jokoon
很高兴听到它运作正常。我刚刚阅读了您提供的链接上的评论。关于您之前的问题,如果它是关于模板导出的最后一步,那么我认为它不会做到。 - CristiFati
问题:我能建立一个使用动态运行时的静态库,反之亦然吗? - Mr. Developerdude
1
是的。默认情况下,libprotobuf 的构建方式决定了如何使用 VCRTLib。构建类型由 2 个标志控制。以下是我如何使用动态 VCRTLib 构建静态 libprotobuf 版本的方法:-Dprotobuf_MSVC_STATIC_RUNTIME=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFF。我应该将这个信息添加到答案中。 - CristiFati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接