前言

其实the Universal CRT(通用C运行时库)已经不能算是“下一代”,因为它已经在前两年伴随着Visual Studio 2015和Windows10发布。但是由于之前使用VS2015开发的应用还较少,关注的人也少,相关的文章更少。所以笔者想干脆用一篇文章,深入浅出的介绍一下Universal CRT。

什么是CRT?

CRT,也就是“C Runtime”,中文译为:C运行时。我们一直称为C Runtime Library(C运行时库)。它主要负责实现C程序在运行时的各种抽象功能实现。不同的C编译器会有不同的具体实现程序,比如Windows下常见的微软的VC编译器的msvcrt.dll;GCC/Glibc的crt0.o、crti.o等。具体到二进制表现形式,可以是动态库,也可以是静态库。

CRT一般会至少负责以下功能:

  • 实现C库函数。比如:printf、fopen等
  • 初始化程序启动所需要的一些功能
  • 异常处理
  • 多线程处理

CRT也可以看成是一个操作系统抽象层。大家都知道,每一种操作系统都有自己的API或者是系统调用。像C语言之所以能够跨平台(代码级),就是因为我只需要用C库函数写程序,CRT会转化为相应平台的具体实现的API来处理。

下面我们要着重讨论的是Windows下的开发工具Visual C++配套的CRT,以下将统称为“VC运行时库”。

VC运行时库的历史

(注:上表中的划代是作者自己的划代方式)

第一代

我记得我最早学C语言还是上大二(2002年)的时候,那时可是VC的黄金年代。额~~应该说是钻石年代。大家都在学习VC++6.0,一个堪称经典的版本。VC++6.0是为了配合Windows98的应用软件开发而发布的,发布的年代也差不多就是98年前后,所以内部有些文件又称之为VC98。

到这个时候,VC运行时库文件只有一个叫msvcrt.dll的,一般在c:\windows\system32下,大家发行软件产品的时候,基本上不需要考虑是否需要带个什么库,因为默认操作系统都会自带的。

逐渐的,大家发现一个非常棘手的问题,那就是随着功能的增多,不同版本的msvcrt.dll支持的功能和函数不同,版本的识别和兼容成了难题。当时很多安装包为了自己的需要,会不停的覆盖系统的msvcrt.dll,导致运行不稳定。如果把WinXP系统的msvcrt.dll覆盖了Win98的,那会导致WinXP不稳定。所以后来就发展除了第二代的CRT。

第二代

自从微软发布Visual Studio.Net 2002/2003开始,为了解决日益增长的功能和DLL版本兼容性问题,微软给现有的运行时库加上了版本号,并且把运行时库拆分为多个文件,比较常见的是msvcrXXX.dll和msvcpXXX.dll。其中XXX是版本号。

版本号的定义和文件命名如下表所示:

其中msvcrXXX.dll(注意不是msvcrtXXX.dll)一般负责实现C语言的基础特性,如程序启动、异常处理、库函数等功能。msvcpXXX.dll负责标准C++库的相关实现。大家可以看看其中的导出函数就能明白。

在这一阶段中,开发者想要发布软件产品,就必须确保目标计算机上必须已经安装了相应版本的运行时库。一般情况下,微软推荐大家使用运行时库安装包来进行安装。对于大型软件为了预防万一,都会附带vcredist.exe安装包。但是这种方法很不受待见。因为现在大多数软件都是通过互联网进行分发,对安装包大小很敏感。每次打包附带一个运行时库包,既增加了用户的下载时间,又增加了推广成本。

现在大部分软件的做法是将这两个dll放在软件目录下发行。但是问题又来了,光是VC2008的msvcr90.dll的就有N个版本,不同语言有不同版本,随着update增加的修正版本也很多。大部分情况应该不会有问题,但笔者就曾经遇到过加载错误版本的运行时库而程序崩溃又查不出问题的经历。这个时候微软要求必须使用manifest文件来指定加载的VC运行时库版本。直到VC2010,由于编译器内置了manifest,所以就不需要额外提供。

第三代

自Visual Studio 2010开始,微软大力改进了很多C++特性,陆续在2012、2013、2015版本中增加了对C99、C++11、C++14、C++17等标准的支持,使得C++库的功能成倍增加。这种小步快跑的更新模式,使得如何有效的让VC运行时库向前和向后兼容而不破坏现有的软件组件的问题变得异常突出。再加上让VC运行时库能够更好的支持Win8/10提倡的PC和移动设备并举的理念,微软团队决定在Visual Studio 2015对VC运行时库进行重构。然后“the Universal CRT”就应运而生了。

the Universal CRT

the Universal CRT(以下简称UCRT),顾名思义,意思为“通用C运行时库”。关键就在“通用”这两个字上。早期的设计理念就是要把相对通用的功能独立出来。这个概念最早在Visual Studio 14 (即vs2015)的CTP1[1]发布的时候提出来[2]。VS很神奇的跳过了13.0这个版本,直接从12.0(vs2013)跳到了14.0(vs2015),估计是因为欧美人把13这个数字认为是不吉利的有关。尽管UCRT的版本号称是1.0,但真实的VCRuntime还是14.0。

早期的设计

当vs2015还在CTP阶段时,微软的设想是将VC运行时库拆分成三部分。
vcruntime140.dll 包含运行期需要处理的功能,如:进程启动、异常处理、以及耦合到相关编译器的功能。

appcrt140.dll包含所有平台上都可用的所有功能,且以后保持这部分CRT的向后兼容性。包括:堆、数学库、stdio库、locale库、大多数字符串操作函数、时间库和一些其他功能等。

desktopcrt140.dll包含所有只能由桌面应用程序使用的功能,且以后保持这部分CRT的向后兼容性。包括:处理多字节字符串、exec和spawn进程管理函数、direct-to-console I/O函数的功能等等。

正式版本

在最终发布正式版的时候,微软将appcrt140.dll和desktopcrt140.dll合并为一个不带版本号的程序库:ucrtbase.dll。它对应的Debug版本的命名是ucrtbased.dll。这个后来被正式命名为“the Universal CRT”。

API Sets for Universal Windows Platform (UWP) apps

令很多人吐槽的是,UCRT并不只是一个DLL,它还附带了一堆以“api-ms-”开头的DLL程序文件,且有40个之多!可以看到,这些DLL导出了几乎所有的win32api。这其实是微软在Windows10中大力推动的“Universal Windows Platform (UWP) apps”即“通用Windows平台应用”的api接口[3]。这些dll有些默认为“delay load”,也就会是被延迟加载。一般基于UCRT编译的程序,不是直接调用ucrtbase.dll,而是调用VCRuntime140.dll和UWP apis来间接调用。

基于UCRT程序的部署方法

什么程序是基于UCRT的?

如果你是用Visual Studio 2015和2017来编写C或C++程序,那么就已经是基于UCRT的。

需要关注的程序模块

VCRuntime140.dll 这是VC运行时库和编译器相关的必备模块,必须存在。
msvcp140.dll 如果你写的程序含有C++标准库的代码,那就必须存在。
ucrtbase.dll和api-ms-**.dll 必须存在。

部署方法

第一种

微软强烈推荐使用vcredist.exe来给目标机安装相应的文件。它会安装所有对的UCRT文件和必备组件。这是最省事儿便捷的方法。

但是vcredist_x86.exe和vcredist_x64.exe就各有近14MB的体积!大型程序发布的时候可能无所谓,而很多很多基于互联网发布的程序,却不可能这么干。互联网程序对安装包的大小很敏感,这直接影响最终用户终端的到达率和推广成本。

可参考PHP7.1的Windows版的下载页面和安装包。

第二种

程序自带VCRuntime140.dll和msvcp140.dll,再给系统打基于msu的KB2999226补丁。KB2999226补丁会给系统安装UCRT。一般情况下,Windows10已经自带了UCRT,不需要额外打补丁。

这种方法不适用于WinXP系统。而在中国WinXP系统还有很大保有量,大家都不会轻易放弃这个庞大的用户群的。

可参考Python 3.5的Windows安装包。

第三种

现在互联网程序大多使用的是app-local的部署模式,意思就是把依赖库放在自己程序目录下,既不会跟别的应用软件冲突,又方便了软件分发。

起初,微软并没有打算针对UCRT程序继续这样的部署模式。但是后来大家反响比较强烈,所以在Windows 10 SDK发布的时候,把UCRT和UMP的相关dlls都一起发布了。这个目录一般是“C:\Program Files (x86)\Windows Kits\10\Redist\ucrt”。

你也可以在Visual Studio 2015的安装目录下找到VCRuntime140.dll和msvcp140.dll。这个目录一般是“C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT”。

有些开发者一开始可能会被ucrt目录下的四十几个文件吓到,不过还好都不大,打包压缩以后都很小。

可参考Visual Studio 2015配套的Remote Debugger 调试工具。这个工具由于要求是“standalone”的,所以就是用此方法部署的。可以在这里找到:“C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Remote Debugger\x86”。

UCRT展望

UCRT的确给我们带来了很多全新的概念,也给我们带来了少许的不适应。但这毕竟是未来的发展方向。基于UCRT的Visual Studio 2015给我们带来的众多新的C、C++语言标准的支持,我相信以后C++程序会变得更加强大。按照网上的一些说法,未来不排除会对VCRuntime模块再次优化重构的可能性,这个我们只能拭目以待了。Visual Studio 2017即将发布,我们也将继续跟进UCRT的发展方向。

[1] CTP 即Community Technology Preview,译为社区技术预览版,一般是微软开发软件的早期对内测试版
[2] 参考https://blogs.msdn.microsoft.com/vcblog/2015/03/03/introducing-the-universal-crt/
[3] 参考:https://msdn.microsoft.com/zh-cn/library/mt186421.aspx

作者简介:陈建明,目前就职于上海恺英网络科技有限公司,C++专业组组长。十多年来,尽管各种编程语言均有涉猎,但主要一直致力于Windows C++平台的各种应用架构、开发和前沿技术的研究。
责编:何永灿(heyc@csdn.net)

下一代VC运行时库——Universal CRT相关推荐

  1. VC运行时库(/MD、/MT等)

    VC项目属性→配置属性→C/C++→代码生成→运行时库 可以采用的方式有:多线程(/MT).多线程调试(/MTd).多线程DLL(/MD).多线程调试DLL(/MDd).单线程(/ML).单线程调试( ...

  2. VC 运行时库 /MD、/MDd 和 /MT、/MTd

    这里总结下他们的区别,后面的那个'd'是代表DEBUG版本,没有'd'的就是RELEASE版本了. 首先说/MT /MT是 "multithread, static version &quo ...

  3. VC++运行时库整理

    C(C99)运行库CRT C++运行库CPRT 静态 动态 托管 静态 动态 通用 libucrt.lib ucrt.lib ucrtbase.dll - - - 用以支持VC++的基础库函数 lib ...

  4. 一文带你弄懂Visual Studio:运行时库及MT/MTD、MD/MDD

    一文带你弄懂Visual Studio:运行时库及MT/MTD.MD/MDD 引子 什么是Runtime Library? Runtime Library和运行库 MT MTD MD MDD的关系 静 ...

  5. VC C运行时库(CRTL)的几个版本及选用

    VC C运行时库(CRTL)的几个版本及选用Michael 2006年7月27日于突尼斯 VC++ C运行时库(以下简称CRTL)是指LIBC.LIB/LIBCMT.LIB/MSVCRT.LIB以及他 ...

  6. /MD, /MDD, /ML, /MT,/MTD(使用运行时库)

    1. VC编译选项 多线程(/MT) 多线程调试(/MTd) 多线程 DLL (/MD) 多线程调试 DLL (/MDd) 2. C 运行时库                              ...

  7. /MD, /MDD, /ML, /MT,/MTD(使用运行时库) .

    1. VC编译选项 多线程(/MT) 多线程调试(/MTd) 多线程 DLL (/MD) 多线程调试 DLL (/MDd) 2. C 运行时库                              ...

  8. C语言标准及C标准库、运行时库简介

    1.C语言标准: 1978 年,Dennis Ritchie 和 Brian Kernighan 合作推出了<The C Programming Language>的第一版(著作简称为 K ...

  9. C运行时库和标准C++库

    先理解运行时库,就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数.VC提供的 C run-time libra ...

最新文章

  1. linux c程序中获取shell脚本输出
  2. 函数传参string_C/C++的三种函数传值方式及其区别
  3. 10-11-根据文章标题搜索文章
  4. 日期选择窗口_Excel攻略|添加日期、智能填充、多窗口同步、两列(行)交换
  5. 【反编译】apk反编译之修改贴吧发贴回帖时显示的地理位置
  6. 本周最新文献速递20210801
  7. 2022年8月最新运维面试题-服务器上下架流程
  8. 华为服务器维修报告,服务器运维报告
  9. 逻辑 - 联言命题,选言命题
  10. 关于STM32F105/107时钟配置详解
  11. 【IPC通信】key_t键和ftok函数
  12. 小白重装系统教程_小白一键重装系统win10教程
  13. ppt 深度学习绘图_最全中文深度学习入门书:小白易入,课程代码PPT全有 | 复旦邱锡鹏出品...
  14. 夏普第二季度亏损数十亿日元 光伏业务持续陷入苦战
  15. 卡迪夫大数据专业排名_2019英国大数据解析
  16. 2.4 找准位置,水滴石穿
  17. 贵州计算机学业水平考试基础知识,贵州省高中信息技术学业水平考试大纲.pdf...
  18. OpenCV常用操作
  19. C语言函数指针【第022讲】
  20. 真正的AI就是要Easy,看汽车零部件企业如何快速解锁AI应用

热门文章

  1. 从ERP到BI 中国企业信息化步入新时代(转)
  2. 【海岛奇兵】各个版本的安装包
  3. 不可能为条目xx GBB 1020 ZBR 3509 确立账户
  4. Dell Inspiron 1526 BUG多多 购买请慎重
  5. 故障模块名称: NetdiskExt64.dll的解决之法
  6. mysql优化 个人笔记 非礼勿扰 -m07
  7. css图片向右移动w3c,W3.CSS Images (图像)
  8. SQLPro for MSSQL for Mac(MSSQL数据库客户端)
  9. 大学新生考英语和计算机,大学:为什么大一新生考英语四级相对容易?这是我见过最好的答案...
  10. MATLAB--数字图像处理 添加高斯噪声