Visual Studio 2015 运行时依赖项或如何摆脱通用 CRT?

36

使用Visual Studio 2015编译了几个.dll文件,并尝试在一些旧的Windows 7 / 64位上部署。还尝试猜测哪些dll文件需要应用程序启动并复制MSVCP140.DLL和VCRUNTIME140.DLL - 但应用程序无法加载vs2015 dll。开始分析问题-依赖项检查器显示来自以下dll文件的依赖项:

API-MS-WIN-CRT-MATH-L1-1-0.DLL
API-MS-WIN-CRT-HEAP-L1-1-0.DLL
API-MS-WIN-CRT-CONVERT-L1-1-0.DLL
API-MS-WIN-CRT-STRING-L1-1-0.DLL
API-MS-WIN-CRT-STDIO-L1-1-0.DLL
API-MS-WIN-CRT-RUNTIME-L1-1-0.DLL
API-MS-WIN-CRT-FILESYSTEM-L1-1-0.DLL
API-MS-WIN-CRT-TIME-L1-1-0.DLL

尤其令人惊讶的是,根据我最好的理解,CRT负责启动dll/exe,但不提供任何高级服务。

好吧,我尝试找出如何摆脱它们或至少将它们最小化。

找到一篇文章: https://blogs.msdn.microsoft.com/vcblog/2015/03/03/introducing-the-universal-crt/

它提到了发布静态库 - 所以我想我可以链接到它们并摆脱*L1-1-0.DLL*依赖地狱,但无论我尝试了什么 - 我都没有成功。我已经尝试链接到libvcruntime.lib、libucrt.lib、libcmt.lib,尝试禁用链接器选项“/nodefaultlib:vcruntime.lib”,甚至尝试添加包含目录$(UniversalCRT_IncludePath),并覆盖一些定义,因为我猜测它们会起作用 - 我所有的尝试都没有帮助。

作为中间解决方案,我回退到使用Visual Studio 2013,其中CRT dll只有两个:msvcp120.dll,msvcr120.dll。

当然,您可能会建议安装Visual Studio 2015运行时,但我们的一个要求是支持独立的可执行文件 - 它可以在没有任何安装的情况下工作 - 因此现在不考虑附加的安装。

除了等待Visual Studio 2017到来以外,您能给我推荐别的什么吗?


5
CRT 不仅仅用于启动 DLL 或 EXE 文件,它还提供了整个 C 库的实现,以及某些语言和编译器功能。除非你重写应用程序以不使用其中任何内容,否则你需要全部使用。 - Ross Ridge
1
好的,尽管命名不同 - crt 以前是一个相当简单的启动器,用于启动构造函数/析构函数 - 我知道这一点,因为我已经改变了它的行为 - 可以在这里看到:http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI但是,在vs2015中,他们是否决定将 msvcp140.dll、msvcr140.dll 拆分成100个小dll? - TarmoPikaro
2
不,CRT一直都是我所描述的那样。crtdll.c文件只是CRT的一小部分,它还包括许多其他内容,例如在您链接的Code Project文章中描述的printf的实现。唯一的区别是现在CRT已被分成单独的库和DLL。如果您想知道他们为什么这样做,可以尝试阅读您在帖子中链接的MSDN博客条目以及它开头引用到的博客条目。作为奖励,如果您花时间阅读到最后,您还会找到解决问题的方法。 - Ross Ridge
3
这是Dependency Walker的一个缺陷,很长时间以来没有进行维护,并且不知道最近Windows加载器的创新,比如这些MinWin转发器。你需要停止使用它。SysInternals的Process Monitor可以告诉你为什么找不到程序所需的DLL文件,你可以看到程序搜索DLL的过程以及搜索的位置。 - Hans Passant
1
我们有一个要求,需要在Windows 7/8/10上无需任何安装即可运行我们的应用程序。到目前为止,我们已经提供了crt dll以及其他.exe/.dll文件 - 可执行文件启动得非常好。但是现在 - 通用crt - 如何将其回溯到Windows 7/8 - 以便不需要安装?通用是否意味着它将与Windows 11向前兼容?还是他们会发明另一个超级通用兼容便携式crt? - TarmoPikaro
显示剩余2条评论
6个回答

42

不可以摆脱它们,但是我通过设置编译器选项C/C++>代码生成 > 运行时库能够静态链接到它们。

  • 对于调试版本:/MDd更改为/MTd
  • 对于发布版本:/MD更改为/MT

这将删除所有API-MS-WIN-CRT-*和运行时dll引用,并导致所有CRT代码被静态链接。

有关新的VS2015通用CRT(动态和静态)的详细信息,请参见: https://msdn.microsoft.com/en-us/library/abx4dbyh.aspx

请注意,唯一的其他选项是使用旧编译器进行编译(比如病毒制作者), 而不是选择较新的编译器,因为Microsoft承诺任何较新的编译器版本都具有相同的UCRT要求。


2
你在虚拟机上测试过还是在实际的Windows 7上测试过?我在所有项目中都默认设置了这个 - 包括调试和发布配置,但这并不能解决问题。最有趣的是,如果你用十六进制编辑器搜索API-MS-字符串,你找不到它。但VCRUNTIME140.DLL有这个依赖关系,我猜它使用了某种改变的加载方式,因为依赖项检查器认为它是我的.exe依赖项。但VCRUNTIME140.DLL仍然引用API-MS dll's。必须切断VCRUNTIME140.DLL的依赖关系。 - TarmoPikaro
1
请仔细检查项目设置,确保选择了/MT代码生成选项。我已经确认在这种情况下vcruntime140.dll的引用将会消失。 - Ton Plooij
经过项目的双重检查,你是正确的——我确实使用了/MD。但我记得/MT从未成功地为我工作过——你知道我们使用MFC,而MFC和/MT会相互冲突。但在vs2015中可能已经改善了这种情况——也许我会再次尝试链接。我感觉这是一段漫长的坎坷之路... - TarmoPikaro
3
首先,我错了。其次,我是正确的。 可以使用 /MT(在 premake5 配置标志 flags { "StaticRuntime" } 中)来链接 MFC - 但是 /MT 和托管代码不兼容,而我不幸在自己的代码中使用了它。 2>cl: 命令行错误 D8016:"/clr"和"/MT"命令行选项不兼容。 - TarmoPikaro
在Win10上使用VS2017,按照您建议的步骤操作后,我仍然遇到了完全相同的问题。 - kakyo
显示剩余2条评论

9
"I too was struggling with the static linking of a solution that had multiple components/project library dependencies importing functions from various parts of MSVCRT, UCRT and Kernel. The hope was to simply copy the resulting EXE where it was needed (it was not a product that would justify a full MSI installation). After almost giving up, I found that the best solution was to follow the guidelines hidden in the Universal C Runtime announcement, specifically:

We strongly recommend against static linking of the Visual C++ libraries, for both performance and serviceability reasons.

"
只需删除您尝试的所有“特殊”链接器选项,回退到/MT|/MD(多线程CRT DLL Release|Debug)运行时库选择,它就可以在任何地方工作,例如较新的Windows 10工作站、2012 R2服务器和Windows 7。只需按照微软的建议安装/重新分发MSVCRT(VC_Redist*.exe)KB2999226(通过Windows Update获取UCRT),因为正如他们所说:

通用CRT是Windows操作系统的组成部分。它作为Windows 10的一部分包含在其中,从1月技术预览版开始,可通过Windows Update在旧版本的操作系统上使用。

因此,逻辑上,我们的C++解决方案为客户增加的唯一附加部署依赖项是MSVCRT,因为在更新/维护良好的计算机上应该已经有UCRT了。当然,这增加了一些不确定性;您不能只是复制EXE并在任何机器上运行,无论好坏。
如果您制作了像MSI这样的良好部署包,那么在使用WIX等工具时很容易包含。另外需要注意的是,自最近的SDK以来,您可以在本地包含40多个DLL文件,但这并不能满足安全更新原则,所以我不会这样做。
这真的是唯一支持的方法,在此处查看另一个示例。本文还建议我们链接到“mincore_downlevel.lib”,这是一个重要的提示,关键在于是否出现这些“api-ms-win *”缺失DLL错误。例如:
  1. 将项目SDK版本设置为10,并链接mincore.lib = 只能在Windows 10上运行,无法在Windows 8.1 / 2012 R2或Windows 7 / 2008 R2服务器上运行。
  2. 将项目SDK版本设置为8.1,并链接mincore.lib = 可在Windows 10和8.1 / 2012 R2服务器上运行,但无法在Windows 7 / 2008 R2服务器上运行。
  3. 将项目SDK版本设置为10,并链接mincore_downlevel.lib = 可以在所有版本上运行!

总结:

  1. 不要进行静态链接,保留项目设置中默认的DLL C运行时库。
  2. 您不需要旧版SDK,可以使用最新的Windows 10 SDK进行开发,但如果想支持较旧的Windows版本,则必须链接“mincore_downlevel.lib”而不是“mincore.lib”。
  3. 为了方便使用,将此添加到targetver.h或stdafx.h中,这也记录了您的选择(删除另一行):
// Libraries
#pragma comment(lib, "mincore.lib")             // Lowest OS support is same as SDK
#pragma comment(lib, "mincore_downlevel.lib")   // Support OS older than SDK

2
请注意,微软明确表示“链接到MinCore.lib或MinCore_Downlevel.lib的二进制文件不适用于Windows 7、Windows Server 2008 R2或更早版本。需要在早期版本的Windows或Windows Server上运行的二进制文件不能使用MinCore.lib或MinCore_Downlevel.lib。” https://msdn.microsoft.com/zh-cn/library/windows/desktop/dn505783(v=vs.85).aspx - Othrayte

7

(更新于2016年11月10日)。

可以通过静态链接来摆脱通用CRT,稍后我会讲到这个问题,但是如果你继续使用通用CRT,我们先来看看。根据文章https://blogs.msdn.microsoft.com/vcblog/2015/03/03/introducing-the-universal-crt/ - 可以使用以下文件夹中的通用crt dll分发启动应用程序:C:\Program Files (x86)\Windows Kits\10\Redist\ucrt

列表中共有41个文件,总大小为1.8 Mb。(64位平台的示例)

当然这还不够,你还需要从以下文件夹获取vcruntime140.dll和msvcp140.dll:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.CRT

因此,在你的应用程序之外,你将额外提供43个dll。

也可以在应用程序内部静态编译ucrt库,这样就不需要43个dll了 - 但是静态链接是否适用于链接之后取决于你的应用程序 - 使用了多少dll和哪些api。通常情况下,当ucrt链接到两个不同的dll时,它们不一定共享相同的全局变量,这可能会导致错误。

你需要链接vcruntime.lib / msvcrt.lib,但这还不够 - 还有额外的_VCRTIMP=_ACRTIMP=定义需要禁用从ucrt中获取函数。

如果你使用premake5,可以像这样配置你的项目:

defines { "_VCRTIMP="}
linkoptions { "/nodefaultlib:vcruntime.lib" }
links { "libvcruntime.lib" }

紧随其后:

defines { "_ACRTIMP="}
linkoptions { "/nodefaultlib:msvcrt.lib" }
links { "libcmt.lib" }

微软没有对宏定义进行文档化,因此未来可能会发生更改。

除了您自己的项目之外,您还需要重新编译正在使用的所有静态库。

至于Boost库-我已经成功地使用b2.exe boostrapper编译了Boost库

boost>call b2 threading=multi toolset=msvc-14.0 address-model=64 --stagedir=release_64bit --build-dir=intermediate_64but release link=static,shared --with-atomic --with-thread --with-date_time --with-filesystem define=_VCRTIMP= define=_ACRTIMP=

在解决链接问题时,请注意由于dllimport关键字的使用而导致未解决的__imp*函数名称 - 如果您链接到libvcruntime.lib,则不应该有任何__imp*引用。


在Boost论坛上发布了问题:https://sourceforge.net/p/boost/discussion/127795/thread/f74a4d33/?limit-25#47c9 - TarmoPikaro

2
我曾经为了找出运行在Visual Studio 2015中构建的应用程序所需的运行时DLL而苦苦挣扎。
以下是我发现的一些内容,可以让VS-2015构建的应用程序运行: 注意:根据您系统处理器的体系结构(x86、x64等)放置dll版本。

2

设置: 配置属性 - 高级 - 使用MFC - "在静态库中使用MFC" 对我有效(对于控制台应用程序而言,不是 MFC/ATL 应用程序)。


1
我做了这个,我的“api-ms-win-xxx.dll缺失”这种_runtime_错误变成了“__imp__SetBkMode”缺失这种_linktime_错误。(不要问我这怎么说得通。)然后我知道该去哪里找问题并立刻解决了它。 - adigostin

0

如果您不打算用自己的运行时(Runtime)取代它,那么以下内容都无关紧要:

  • 您启用/禁用了运行时类型(Runtime Typing)
  • 您启用/禁用了 C++ 异常(C++ Exceptions)
  • 无论您是否将运行时库设置为“多线程 DLL”(Multithreaded DLL),这并不重要(将其设置为非 DLL 仍会将其构建到您的二进制文件中)

您需要确保的唯一事情是:不要使用它的任何功能,Visual Studio 就会自动取消与之的链接。也就是说,没有断言,没有对 string.h 或 stdio.h 中的任何内容的调用,完全没有。编译器可以替换为其自己的内部函数(intrinsics),如 static_assert 等编译器检查则可以。


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