如何解决程序/C++Dll的兼容性问题

如何解决程序/C++Dll的兼容性问题

本文将尝试解决程序与DLL在不同客户机上运行存在的兼容性问题

前言

前面的五篇文章已经将程序的核心部分全部搞定,由于我们调用了一些系统API,所以这很有可能导致程序在其他不同的客户机上无法正确运行。其实关于程序的兼容性提高在之前选择到底使用何种方法来实现某一功能时已经相当于做过一次了,由于所有的核心功能都是封装在C++DLL中的,那么C++DLL兼容性直接决定了程序的兼容性,所以提高C++DLL兼容性成为了至关重要的问题。

1)先尝试在目标客户机试运行

注意:在开发者的电脑上测试是绝对通过的,能在开发者电脑上测试通过不代表客户机能运行,由于开客户机不一定有开发者电脑的大量程序运行必要的支持框架和组件,所以先要弄清楚程序需要哪些支持框架、组件、Dll,然后再搞清哪些支持框架是客户机肯定拥有的,哪些是客户机不一定拥有的

要在客户机上测试,那么什么样的客户机是合理的: 
Windows操作系统,这是必须的 
编写程序界面的语言是C#,编写程序核心的语言是C++,两种语言都需要Microsoft .NET Framework的支持,由于这个程序在编写当初就没有想支持XP的意思,所以这两种语言都选用了Microsoft .NET Framework 4.5版本(XP最高支持到Microsoft .NET Framework 3.0),其实Microsoft .NET Framework 4.5算是客户机上必有的支持框架了。

大概现在就知道需要这个了,于是打开VM新建一个虚拟机,安装WIN7,再装入一个Microsoft .NET Framework 4.5支持框架,把程序拷贝进去,启动。

这里写图片描述

由于程序启动不用调用DLL.dll所以程序界面是可以打开的,继续测试功能,单击“连接”。

这里写图片描述

明明已经把DLL.dll放到了程序运行目录下,但它还是提示找不到指定模块DLL.dll

这个提示并不是找不到DLL.dll,其实是想表明DLL.dll所引用的DLL找不到,那么必须弄清楚这个DLL.dll还引用了什么其他DLL

2)查看DLL.dll引用了哪些DLL

用DLL函数查看器打开编写的DLL 
查看DLL的引用,将DLL的引用信息输出TXT文件查看

这里写图片描述

这里显示一共引用了5个DLL,功能分别是:

系统支持的: 
wlanapi.DLL:管理WLAN的系统DLL 
RASAPI32.DLL:管理拨号的系统DLL 
KERNEL32.DLL:系统内核DLL 
Visual Studio 2015 C++支持的: 
VCRUNTIME140.DLL:Visual Studio 2015 生成的 C++ 应用程序所需的运行时组件。 
api-ms-win-crt-runtime-l1-1-0.dll:Visual Studio 2015 生成的 C++ 应用程序所需的运行时组件。 
注:安装Visual C++ Redistributable Package就会为Visual Studio 2015 C++ 生成的 C++ 应用提供运行支持

通过上面的DLL分类大概可知是由于DLL.dll找不到Visual Studio 2015支持的两个DLL导致的

依据我们的DLL引用DLL时的路径寻找方法(在上一篇文章中提到过),只要把这两个DLL放到和DLL.dll相同目录下应该就OK了。

3)依据引用的DLL名称尝试找出被引用的DLL

在C盘搜索VCRUNTIME140.DLL,结果如下:

这里写图片描述

竟然有么多,这就很尴尬了,怎么确定它调用的是哪个


没办法,只能再上一个软件procexp64_V16.02(进程查看器),他可以看到所有进程调用的所有DLL以及这些被调用的DLL的位置

在开发者电脑上启动“闪讯破解”

打开procexp64_V16.02,找到“闪讯破解”,按下Ctrl+D显示闪讯破解所调用的DLL的信息

这里写图片描述

在这里只找到了VCRUNTIME140.DLL没有找到api-ms-win-crt-runtime-l1-1-0.dll;先根据procexp64_V16.02的DLL路径显示拷贝VCRUNTIME140.DLL到虚拟机应用程序下;由于没有找到api-ms-win-crt-runtime-l1-1-0.dll的真实调用路径,只能是去C盘搜索这个DLL然后随便拷贝一个过来试试了。

这里写图片描述

现在能确定系统支持的3个DLL和VCRUNTIME140.DLL是没有问题的,只能尝试运行程序

这里写图片描述

果然报错了,这次不是”找不到指定模块了“而是“试图加载不正确的程序”,这是由于引用了不正确的DLL导致的,估计就是api-ms-win-crt-runtime-l1-1-0.dll出问题了。但是毕竟C盘有这么多api-ms-win-crt-runtime-l1-1-0.dll,总不可能都一一拷贝过来尝试,看来只能另寻他法了。

这里写图片描述

4)使用静态编译生成DLL

右键点击DLL项目名,打开DLL项目属性

这里写图片描述

配置属性–C/C++–代码生成–运行库

这里写图片描述

这里的运行库有4个选项:

后面的那个’d’是代表DEBUG版本,没有’d’的就是RELEASE版本了

多线程MT的程序来说,其连接的是libcmt.lib,该文件属于C语言运行时库,整个lib都会连接到PE文件当中。

多线程MD的程序链接的却是类似msvcpXXX.dll,该文件属于微软运行时库,也就是说如果是多线程MD编译出来的文件运行时都会加载相应版本的运行时库,当如果找不到运行时库就会报错而无法运行,同时如果运行时库不匹配也会出现各种意料之外的崩溃或者程序根本跑不起来等情况

从上面的说明可以看出由于使用的是MD方式编译,所以在运行时会调用更多的用于支持VS2015C++应用运行的DLL,为了减少调用DLL数量,选择MT编译。

注:千万不可以调用DEBUG的DLL,否则加载模块照常会出错。

选用MT后重新编译,用DLL查看器打开新生成的DLL,输出被引用DLL文件信息

这里写图片描述

发现关于VS2015的两个DLL都已经消失,只剩下三个系统的DLL

拷贝到虚拟机,尝试

单击”连接“按钮后如果能成功引用DLL中的函数,程序会进行编辑框输入文本检验

这里写图片描述

程序成功的引用了DLL中的函数,进行了编辑框输入文本检验 
同时也发现,新编译的DLL大小变大了许多

5)使用 多线程MT 方式编译的弊端

成功运行程序后发现DLL变大了许多,这样就不是很好了,这是由于多线程MT编译的时候整个lib都会编译到DLL文件当中导致的。其实多线程MT编译不单单是这种缺点:如果是通过多线程MT编译方式出来的程序,那么A模块中申请的内存到B模块中释放就会出现问题,这是非常致命的。其实绝大多数软件都是采用多线程MD方式编译,如果找到他们目录很容易发现上面提到的运行时库。因为这样一来编译出来的文件小,所有运行时库统一,同时也让内存管理简单化,省去了跨模块内存访问带来的各种bug。所以多线程MD还是很好用的,能不用多线程MT就不用。

6)如何才能让多线程MD方式编译的程序在客户机正常运行

在上面在解释引用DLL各自功能中就提到过,安装Visual C++ Redistributable Package就会为Visual Studio 2015 C++ 生成的 C++ 应用提供运行支持,x64和x86最好都装上,这样程序就可以在客户机上运行了

这里写图片描述

这里写图片描述

最后在说两句

如何提高程序的兼容性

1)对于程序所使用的编程语言的必要运行环境支持必须熟知,比如C#需要安装Microsoft .NET Framework X.X,C++需要安装Visual C++ Redistributable for Visual Studio XXXX(x64和x86)

2)要学会使用DLL函数查看器、depends.exe、procexp64_V16.02来查看程序运行所依赖的dll,并不是所有客户机都有Visual C++ Redistributable for Visual Studio XXXX(x64和x86),把找到的正确的DLL直接加入程序目录。

3)在程序的安装程序中添加运行环境检测,给予没有必要运行环境的客户机强制安装必要的运行环境支持。

BY Sunyday丶若雪 转载请注明出处