对于一部分 Fortran 程序员来说,他们书写代码的目的是计算得到结果。同时也有一部分程序员,他们的目的是发布一个程序(exe或其他)给朋友,同学,甚至是作为商业软件出售给别人使用。
在一部分编译器上,编译后的 exe 再复制到其他计算机上(未安装编译器),会出现类似下面的错误:
这是由于编译器编译后的程序,会依赖这些DLL。
这些问题,可以通过一些方式来解决。Fortran是编译型语言,编译好的程序(exe等)是完全可以独立于编译器的。
一. 为什么要依赖一部分DLL
我们书写的代码,并不是就可以独立运行了,有一些通用性的代码需要由编译器来提供。比如读写文件,比如一些常见的数学函数,比如获取命令行参数的代码。这些通常被称为 运行时库(Runtime library)。
在一部分编译器上,编译器会将运行时库(或部分)写入到DLL里。这样做的好处是:
1.减少编译后 exe 文件的大小。
2.提高编译链接速度。
3.提高通用代码的可重复使用。
4.便于升级。
对于这类的编译器,写入到 DLL 里的运行时库,我们编译后的程序就会依赖它。
二. 具体依赖哪些 DLL,如何查找依赖,如何处理依赖?
这个问题,是没有标准答案的。不同的编译器,其依赖的DLL不同。(有的默认情况下并不产生依赖)
咱们有两种方式来检查依赖:
1. 拷贝到其他计算机上,运行,会提示我们缺少某 DLL。在编译的计算机A上搜索这个文件,同时拷贝过去。再次运行,如还提示缺少其他的,依样复制。直到可正常运行为止。
2. 使用一些专业工具。如 Dependency Walker
Dependency Walker 是微软公司出品的一款工具,用于检查各程序间的DLL依赖。它是免费软件,被包含在 VC++ 6.0 中。后期的 Visual Studio 可能没有,需单独下载安装。
下载安装后,运行 Dependency Walker,打开编译后的 exe 文件。可看到类似上图的结果。
请先确保右键的 Full Paths 是勾选状态。(以便于我们区分依赖的 DLL 是操作系统的还是编译器的。)
我们可以看到上图中,fcode.exe 是我们编译后的可执行文件,其依赖了大量的 DLL。但这些中的绝大多数,都是操作系统的,不需要进行任何处理,如上图的粉色框内的DLL。(正常的计算机上都存在)
通常,我们只需要关注 IVF 安装目录下的那些 DLL 就可以了。如图的蓝色框内的两个 DLL:libIFCoreMDD.dll 和 LibMMD.dll 两个文件。
一般只需要复制这两个文件既可。(有些时候需要复制 MSVCR 开头的 DLL)
检查程序的依赖,并不需要每次都进行,通常做一次就知道编译器的习惯了,下一次使用这个编译器发布程序,就可以依样复制这些 DLL。
三. 所依赖的 DLL 复制到哪儿?
一般情况下,所依赖的 DLL 复制到与编译后的 exe 相同文件夹既可,比如上面的例子可以像这样:
四. 能否取消依赖?
这取决于编译器,某些编译器可以通过设置来取消依赖。比如 IVF 可通过如下设置:
对于其他编译器,则要看编译器的功能了,翻阅一下其说明文档会获得相关信息。
需要注意的是,不仅仅是编译器会产生运行时库的依赖,使用了某些函数库,也可能会产生相关的依赖。比如 IMSL 或 MKL 可能会产生对 libiomp5md.dll 的依赖,这是由于这些函数库使用了 OpenMP 技术。对于这些依赖,能否取消,也要看具体的函数库文档。
五. 是否可以使用其他手段把 DLL 打包到 EXE 中?
这个问题已经超越了 Fortran 的范畴,在实际使用中,有一些打包工具可以实现把 DLL 封装入 EXE 中。比如 WinRAR,Enigma Virtual Box,或者一些绿色软件制作工具等。他们是利用了压缩包(解压临时文件),或虚拟机等技术来实现的。
他们的优点是,可以发布一个简单的单文件程序。缺点是,可能会影响执行效率,还可能导致杀毒软件误杀。所以不推荐使用。
六. DLL依赖有没有好处?
显然,是有好处的。但它是针对较大型的程序。
比如 A.exe 使用了 B.dll ,C.dll,D.dll ,同时它还可能会调用 X.exe 。而上述的程序都是用同样的编译器生成,那么A B C D X 中,都有一份通用运行时库 R 的拷贝。
相当于:
A(R).exe , B(R).dll , C(R).dll , D(R).dll , X(R).exe
而如果把 R.dll 单独放出来,则可以大量减少程序的最终尺寸。
相当于:
A.exe , B.dll , C.dll , D.dll , X.exe , R.dll
同时,由于 R.dll 单独放出来,所以只映射一份到内存,也会加速程序的执行。
因此,大型程序(由多个exe或多个dll组成时),笔者建议开启 DLL 依赖。