前提
本文面向Windows任意NT平台可移植方案的讨论,
如果你:
- 不在乎运行时大小
- 面向的是商业用户
那么可以不看。
Visual C++
从Visual C++ 6
以后(不包含VC6
),通过Visual Studio
编译的VC
程序,默认都会link
到msvcrXX.dll
,这个DLL
就是VC运行时
,如果使用了MFC
,还会Link
到mfcXX.dll
(XX
代表VS
的版本号,比如VS 2008
是msvcr90.dll
)
在
VC 2015
之后,更是多了一堆api-ms-win-core
api-ms-win-crt
的补丁
而绝大多数系统默认不会包含这些VC运行时
,
比如:QQ
、TIM
用的是VC2010
,发布QQ
的时候,需要打包一个msvcrXX.dll
到目录,或者是在打包程序中安装vcredist
,这样对于发布一个小型应用来说,运行时就占用了大量包体空间。
而像Delphi
、Go
这类语言开发出来的exe
,虽然不带运行时(运行时打包在exe
中了),但是Delphi
生态圈早已经没落了,而Go
的Windows
的生态圈才刚刚起步
Windows
上面的程序主流只能是VC
。
别和我提什么.Net、Java程序,又不是大型商业软件,可以将运行时打包发布,或者是强制用户安装运行时。
解决方案
所以,这里提出如下方法避免程序连接到这些运行时,让你的程序可移植到任意Windows NT平台而无需捆绑运行时。
0x1. Visual C++ 6
为什么Visual C++ 6
到现在都没死?
因为Visual C++ 6
编译的程序,默认Link
到msvcrt.dll
, 这个dll
包含在Windows 2000
和以上的Windows
中,也就是说,程序可以不捆绑任何运行时,都能运行(当然,你没有用MFC
或第三方dll
)。
下图可以看到一个VC++ 6
编译的 DLL
的依赖,都是系统自带DLL
而msvcrt.dll
这个DLL
很特殊,在个DLL
在Windows XP
和之后的系统中提升为了系统dll
,只允许内核程序调用,但是VC 6
的程序仍然可以调用它。
注意:
msvcrt.dll
并不是一个标准C的DLL,
0x2. 编译为MT
在国内的搜索得到的解决方案,大部分都是如下操作:
项目 点击右键 -> 属性 Properties -> C/C++ -> Code Generation -> Runtime Library
,选择/MT
即可
当然这前提是在Release
模式下,Debug
选这个没意义,毕竟大家也不会发布Debug
的程序对吧。
这样做的目的是将VC
的一些C Lib
打包到目标exe
中,这样程序在发布的时候,就可以不用到运行时了。
- 程序会变大
- 如果项目还包含
DLL
或其他程序,那么每个文件都需要打包一份C Lib
,这样每个文件都会膨胀很大。
0x03. 使用minGW的GCC编译
前提是没使用MFC
。
正常安装minGW
可以在mingw64\bin
目录下看到如下文件
使用如下的语句编译
g++ main.c -Wall -O2 -o "bin\Debug\Hello.exe"
strip "bin\Debug\Hello.exe"
使用
gcc
编译程序,最好编写Makefile
文件制定编译顺序和方法
使用MinGW gcc
编译的exe
,会LINK
到msvcrt.dll
如果使用标准C
,还需要附带libstdc++-6.dll
、libgcc_s_seh-1.dll
。这2个DLL
在mingw64\bin
目录下,复制到exe
目录下可以正常运行。
这中间方式编译出来的文件不会太大,比使用MT要小。
如果希望编译出不带libstdc++-6.dll
的文件,
可以使用如下方法:
// 主要是下面 -c 这个参数,先将cpp输出为中间文件
g++.exe -Wall -c -g main.cpp -o obj\Debug\main.o
// 然后静态连接 libstdc++
g++.exe -static -static-libgcc -static-libstdc++ -o "bin\Debug\Hello.exe" obj\Debug\main.o
strip "bin\Debug\Hello.exe"
这种方式编译出来的文件和MT
一样,会打包相应的C Lib
,所以文件会很大。
对于一些标准C的程序
- 不建议使用
cygWin
来编译,它是模拟linux
程序在windows
上面运行,运行必须带一个cygwin.dll
-
MinGW
则是将程序当做原生程序运行,只调用系统自带的msvcrt.dll
,如果不使用标准C
,可以使用boost
,那么,libstdc++-6.dll
都不用附带
0x04. /NODEFAULTLIB
压轴
这方法是在VS
中,创建一个不使用任何运行时的exe
,所以,连标准C
都不能使用。
比如创建一个控制台程序
然后我们在打开项目属性Debug
模式下大家不要实验了,因为需要Debug
,所以会强制附带msvcrXX.dll
不依赖任何默认库
- 关闭依赖
msvcrXX.dll
- 指定入口为main
这样编译,会报错,因为没有关闭安全检查
编译出来的程序,就什么库都没使用了,并且可以使用C++14
等高级语法,但是需要自己去解决C库
的问题,比如boost
。
并且,这个exe
的安全检查和exception
也被关闭了,你需要自己注意内存安全。
0x05. 变相调用msvcrt.dll
这是某德国人写的一个曲线解决方案,前提是没有使用标准C
,因为上面说过,只要使用标准C
,肯定会调用VC
的相应DLL
,那么需要自己去解决标准C
还是boost
下载地址如下:
https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/msvcrt-light.zip
编译时,连接到msvcrt-light-x64.lib
,待续