编译一个不依赖任何msvcrXX.dll的Windows程序

前提

本文面向Windows任意NT平台可移植方案的讨论,

如果你:

  • 不在乎运行时大小
  • 面向的是商业用户

那么可以不看。

Visual C++

Visual C++ 6以后(不包含VC6),通过Visual Studio编译的VC程序,默认都会linkmsvcrXX.dll,这个DLL就是VC运行时,如果使用了MFC,还会LinkmfcXX.dll
(XX 代表VS的版本号,比如VS 2008msvcr90.dll

VC 2015之后,更是多了一堆 api-ms-win-core api-ms-win-crt的补丁

而绝大多数系统默认不会包含这些VC运行时

比如:QQTIM用的是VC2010,发布QQ的时候,需要打包一个msvcrXX.dll到目录,或者是在打包程序中安装vcredist,这样对于发布一个小型应用来说,运行时就占用了大量包体空间。

而像DelphiGo这类语言开发出来的exe,虽然不带运行时(运行时打包在exe中了),但是Delphi生态圈早已经没落了,而GoWindows的生态圈才刚刚起步

Windows上面的程序主流只能是VC

别和我提什么.Net、Java程序,又不是大型商业软件,可以将运行时打包发布,或者是强制用户安装运行时。

解决方案

所以,这里提出如下方法避免程序连接到这些运行时,让你的程序可移植到任意Windows NT平台而无需捆绑运行时。

0x1. Visual C++ 6

为什么Visual C++ 6到现在都没死?

因为Visual C++ 6编译的程序,默认Linkmsvcrt.dll, 这个dll包含在Windows 2000和以上的Windows中,也就是说,程序可以不捆绑任何运行时,都能运行(当然,你没有用MFC或第三方dll)。

下图可以看到一个VC++ 6编译的 DLL 的依赖,都是系统自带DLL

编译一个不依赖任何msvcrXX.dll的Windows程序_第1张图片

msvcrt.dll这个DLL很特殊,在个DLLWindows XP和之后的系统中提升为了系统dll,只允许内核程序调用,但是VC 6的程序仍然可以调用它。

注意:msvcrt.dll并不是一个标准C的DLL,

0x2. 编译为MT

在国内的搜索得到的解决方案,大部分都是如下操作:
项目 点击右键 -> 属性 Properties -> C/C++ -> Code Generation -> Runtime Library,选择/MT 即可

编译一个不依赖任何msvcrXX.dll的Windows程序_第2张图片

当然这前提是在Release模式下,Debug选这个没意义,毕竟大家也不会发布Debug的程序对吧。

这样做的目的是将VC的一些C Lib打包到目标exe中,这样程序在发布的时候,就可以不用到运行时了。

  • 程序会变大
  • 如果项目还包含DLL或其他程序,那么每个文件都需要打包一份C Lib,这样每个文件都会膨胀很大。

0x03. 使用minGW的GCC编译

前提是没使用MFC

正常安装minGW

可以在mingw64\bin目录下看到如下文件

编译一个不依赖任何msvcrXX.dll的Windows程序_第3张图片

使用如下的语句编译

g++ main.c -Wall -O2 -o "bin\Debug\Hello.exe"
strip "bin\Debug\Hello.exe"

使用gcc编译程序,最好编写Makefile文件制定编译顺序和方法

使用MinGW gcc编译的exe,会LINKmsvcrt.dll
如果使用标准C,还需要附带libstdc++-6.dlllibgcc_s_seh-1.dll。这2个DLLmingw64\bin目录下,复制到exe目录下可以正常运行。

编译一个不依赖任何msvcrXX.dll的Windows程序_第4张图片

这中间方式编译出来的文件不会太大,比使用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都不能使用。

比如创建一个控制台程序

编译一个不依赖任何msvcrXX.dll的Windows程序_第5张图片

然后我们在打开项目属性
Debug模式下大家不要实验了,因为需要Debug,所以会强制附带msvcrXX.dll

不依赖任何默认库

  • 关闭依赖msvcrXX.dll
  • 指定入口为main

编译一个不依赖任何msvcrXX.dll的Windows程序_第6张图片

这样编译,会报错,因为没有关闭安全检查

编译一个不依赖任何msvcrXX.dll的Windows程序_第7张图片

编译出来的程序,就什么库都没使用了,并且可以使用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,待续

你可能感兴趣的:(编译一个不依赖任何msvcrXX.dll的Windows程序)