添加 Boost 使调试构建依赖于“非 D”MSVC 运行时 DLL
-
03-07-2019 - |
题
我有一个恼人的问题,我也许能够以某种方式规避它,但另一方面,我更愿意解决它并了解到底发生了什么,因为看起来这个东西真的会留下来。
故事是这样的:我有一个简单的 OpenGL 应用程序,运行良好:在编译、链接或运行它时从来都不是一个大问题。现在,我决定尝试将一些更密集的计算转移到工作线程中,以便可能使 GUI 的响应速度更快——当然是使用 Boost.Thread。
简而言之,如果我在 .cpp 文件的开头添加以下片段:
#include <boost/thread/thread.hpp>
void dummyThreadFun() { while (1); }
boost::thread p(dummyThreadFun);
, ,然后在尝试启动调试版本时开始收到“此应用程序无法启动,因为未找到 MSVCP90.dll”。(释放模式工作正常。)
现在使用 Dependency Walker 查看可执行文件,他也没有找到这个 DLL(我猜这是预期的),我可以看到我们正在寻找它以便能够调用以下函数:
?max@?$numeric_limits@K@std@@SAKXZ
?max@?$numeric_limits@_J@std@@SA_JXZ
?min@?$numeric_limits@K@std@@SAKXZ
?min@?$numeric_limits@_J@std@@SA_JXZ
接下来,我尝试转换每个实例 min
和 max
使用宏来代替,但可能找不到对它们的所有引用,因为这没有帮助。(我正在使用一些外部库,但我没有可用的源代码。但即使我能做到这一点——我也不认为这真的是正确的方式。)
所以,我想我的问题是:
- 即使使用调试版本,为什么我们还要寻找非调试 DLL?
- 解决问题的正确方法是什么?或者甚至是一种快速而肮脏的方式?
我首先在 Visual Studio 2008 的普通安装中遇到了这个问题。然后尝试安装Feature Pack和SP1,但它们也没有帮助。当然也尝试过Rebuild几次。
我正在使用预构建的二进制文件进行 Boost (v1.36.0)。这不是我第一次在这个项目中使用 Boost,但这可能是我第一次使用基于单独来源的部件。
禁用增量链接没有帮助。该程序是 OpenGL 的事实似乎也无关紧要——当我将相同的三行代码添加到一个简单的控制台程序中时,我遇到了类似的问题(但它抱怨 MSVCR90.dll 和 _mkdir
, ,当我将后者替换为 boost::create_directory
, ,问题就消失了!!)。实际上,只需删除或添加这三行即可使程序正常运行或根本不运行。
我不能说我理解并排(甚至不知道这是否相关,但这就是我现在的假设),说实话,我也不是超级感兴趣 - 只要我能构建、调试和部署我的应用程序...
编辑1: 在尝试构建一个无论如何都能重现该问题的精简示例时,我发现该问题与 传播工具包, ,它的使用是我所有遇到此问题的程序的共同因素。(但是,在开始链接 Boost 内容之前我从未遇到过这个。)
我现在想出了一个最小的程序,可以让我重现这个问题。它由两个编译单元A.cpp和B.cpp组成。
A.cpp:
#include "sp.h"
int main(int argc, char* argv[])
{
mailbox mbox = -1;
SP_join(mbox, "foo");
return 0;
}
B.cpp:
#include <boost/filesystem.hpp>
一些观察结果:
- 如果我注释掉该行
SP_join
A.cpp 的问题就消失了。 - 如果我注释掉 B.cpp 的单行,问题就会消失。
- 如果我将 B.cpp 的单行移动或复制到 A.cpp 的开头或结尾,问题就会消失。
(场景2和3中,调用时程序崩溃 SP_join
, ,但这只是因为邮箱无效......这与当前的问题无关。)
此外,Spread 的核心库已链接进来,这肯定是我的问题 #1 的答案的一部分,因为我的系统中没有该库的调试版本。
目前,我正在尝试想出一些方法,可以在另一个环境中重现该问题。(尽管如果它实际上可以在我的场所之外重复,我会感到非常惊讶......)
编辑2: 好吧,那么 这里 我们现在有了一个包,我可以使用它在几乎普通的 WinXP32 + VS2008 + Boost 1.36.0 安装上重现该问题(仍然是 来自 BoostPro 计算的预构建二进制文件).
罪魁祸首肯定是 Spread 库,我的构建不知何故需要一个相当古老的 STLPort 版本 MSVC 6!尽管如此,我仍然觉得这些症状比较有趣。另外,如果您能真正重现该问题(包括上面的场景 1-3),那就太好了。包装很小,应该包含所有必需的部件。
事实证明,该问题实际上与 Boost.Thread 没有任何关系,因为此示例现在使用 Boost Filesystem 库。此外,它现在抱怨 MSVCR90.dll,而不是像以前那样抱怨 P。
解决方案
Boost.Thread有很多可能的构建组合,以尝试和满足MSVC可能的链接方案的所有差异。首先,您可以静态链接到Boost.Thread,也可以链接到单独的DLL中的Boost.Thread。然后,您可以链接到MSVC运行时的DLL版本或静态库运行时。最后,您可以链接到调试运行时或发布运行时。
Boost.Thread标头尝试使用编译器生成的预定义宏自动检测构建方案。为了链接使用调试运行时的版本,您需要定义 _DEBUG
。这是由/ MD和/ MDd编译器开关自动定义的,所以它应该没问题,但是你的问题描述也是如此。
你从哪里获得预先建立的二进制文件?您是在项目设置中明确选择库,还是让自动链接机制选择适当的.lib文件?
其他提示
我相信我过去曾遇到过与Boost相同的问题。根据我的理解,它发生是因为Boost头使用预处理器指令链接到正确的lib。如果您的调试和发布库位于同一文件夹中并且具有不同的名称,则“自动链接”将显示在同一文件夹中。功能无法正常工作。
我所做的是为我的项目定义BOOST_ALL_NO_LIB(这会阻止标题“自动链接”),然后使用VC项目设置链接到正确的库。
看起来其他人已经回答了问题的升压方面。这里有一些关于 MSVC 方面的背景信息,这可能会避免进一步的麻烦。
C(和 C++)运行时有 4 个版本:
- /公吨:libcmt.lib (C)、libcpmt.lib (C++)
- /MTd:libcmtd.lib、libcpmtd.lib
- /医学博士:msvcrt.lib、msvcrt.lib
- /医学博士:msvcrtd.lib、msvcprtd.lib
DLL 版本仍然需要链接到该静态库(它以某种方式完成所有设置以在运行时链接到 DLL - 我不知道详细信息)。请注意,在所有情况下,调试版本都有 d
后缀。C 运行时使用 c
中缀,C++运行时使用 cp
中缀。看到图案了吗?在任何应用程序中,您应该只链接到这些行之一中的库。
有时(就像您的情况一样),您发现自己链接到其他人的静态库,该静态库被配置为使用错误版本的 C 或 C++ 运行时(通过非常烦人的 #pragma comment(lib)
)。您可以通过提高链接器的详细程度来检测这一点,但它是真正需要寻找的 PITA。“用火箭筒杀死啮齿动物”的解决方案是使用 /nodefaultlib:...
链接器设置以排除 6 个您知道不需要的 C 和 C++ 库。我过去用过这个没有问题,但我不确定它总是有效......也许有人会突然告诉我这个“解决方案”如何导致你的程序在周二下午吃掉婴儿。
这是一个经典的链接错误。看起来你是链接到Boost DLL 本身链接到错误的C ++运行时(还有此页 ,对“线程”进行文本搜索。它看起来像 boost :: posix :: time
库链接到正确的DLL。
不幸的是,我找不到讨论如何选择正确构建的Boost DLL的页面(虽然我找到了三年之久的电子邮件似乎指向 BOOST_THREAD_USE_DLL
和 BOOST_THREAD_USE_LIB
)。
再次查看您的答案,您似乎正在使用预先构建的二进制文件。您无法链接的DLL是 TR1功能包的一部分(该页面上的第二个问题)。 该功能包可在Microsoft网站上找到。或者你需要一个不同的二进制文件来链接。显然 boost :: posix :: time
库链接到未修补的C ++运行库。
由于您已经应用了功能包,我认为我将采取的下一步是手动构建Boost。这是我一直采取的方式,而且非常简单:下载BJam二进制文件,并在库源中运行Boost Build脚本。就是这样。
现在这变得更有趣......如果我只是在源代码中添加它:
boost::posix_time::ptime pt = boost::posix_time::microsec_clock::universal_time();
(连同相应的 #include
东西),然后再次正常工作。所以这是一个快速,甚至不太脏的解决方案,但嘿&#8212;这是怎么回事,真的吗?
从内存中,boost库的各个部分需要您定义一些预处理程序标志,以便能够正确编译。像 BOOST_THREAD_USE_DLL
等等。
BOOST_THREAD_USE_DLL
不会导致此特定错误,但可能需要您定义 _DEBUG
或类似的东西。我记得几年前在我们的boost C ++项目中,我们在visual studio编译器选项(或makefile)中声明了很多额外的 BOOST_XYZ
预处理器定义
检查boost thread目录中的 config.hpp
文件。当您输入 ptime
时,它可能包含一个不同的 config.hpp
文件,然后可能以不同的方式定义这些预处理器。