提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
笔者最近在研究如何用C++调用pcl库的代码,并将代码封装成dll供C#使用,使用的IDE是VS2017。在经过一番努力之后,终于在封装成功,并且在本机上测试通过。但当我把dll分发给别人使用的时候,却出现如下错误:
System.DllNotFoundException:“无法加载 DLL“PointCloudDll.dll”: 找不到指定的模块
可是我明明把所有的Dll都放在了exe的同一级目录下面,为什么还是会出现这个错误呢?
一、错误分析
出现这种错误,一般来说有三种原因。
第一,封装的代码出了问题
第二,dll的位数和目标机的位数不一致
第三,封装的dll引用了其他的dll,分发时没把这些dll也给放进去。
我在本机上测试的时候是没问题的,所以第一点可以排除;自己封装的dll是64位的,目标机也设置了64位,第二点可以排除。那问题就出现在了第三点。
可是我在封装的时候并没有引用其他dll。是的,我们自己是没有使用其他dll,但是电脑自动帮我们引用了相关的dll。这个和vs的dll搜寻路径有关。一般来说,vs的dll搜寻顺序如下:
a. 应用程序所在目录;
b. 系统目录。 GetSystemDirectory 返回的目录,通常是系统盘\Windows\System32;
c. 16位系统目录。该项只是为了向前兼容的处理,可以不考虑;
d. Windows目录。 GetWindowsDirectory 返回的目录,通常是系统盘 \Windows ;
e. 当前目录。GetCurrentDirectory返回的目录;
f. 环境变量PATH中所有目录
注意,这里面的系统目录,不论是32位机还是64位机,一般都是Windows\System32,有人可能会说,不是还有一个SysWOW64目录吗。64位机不应该放在这个目录下面吗。不是这样的,WOW64是Windows on Wondows的简写,它是为了兼容32位的程序而存在的。在64位的操作系统中,32位代码和64位代码是单独运行,有System32和SysWOW64两个库文件夹,甚至有两个注册表。通常的思维,按32位系统的规律,存放64位的dll的文件夹应该叫System64,但实际上,微软为了保持兼容性,64位的dll依然存放在System32文件夹下,同时为了兼容32位,多设置了一个SysWOW64文件夹。
搞明白了这一点,应该明白了为什么会报错了。那是因为自己封装的dll使用了很多pcl库的代码,而我并没有把这些相关的dll放在目录中。但是我在本机上运行的时候,是没有报错的。这是因为我本机上配置好了pcl库的环境变量,所以当调用封装的dll时候,该dll会使用pcl的dll,虽然我并没有把这些dll放进目录中,但vs可以通过环境变量找到这些相关的dll,所以不会报错。
二、解决方案
既然知道是缺少相关的dll导致的问题,那第一件事就是找到缺少了哪些dll,这里需要使用dll分析工具,通过该工具分析自己封装的dll中还引用了哪些dll,这里推荐B站up主Hell8999写的分析工具PE分析器,这是下载链接
通过这个工具,可以分析出我自己封装的dll引用了很多另外的dll
其中,红色箭头指向的是pcl相关的dll,绿色箭头指向的是window系统的dll,一般正常的windows都会有这些dll,然后蓝色的是C++运行时的dll。这里面我只需要把pcl的dll放进目录中就可以了,另外的dll在大部分电脑中都会有。仅仅这样还是不够的,因为这两个pcl的dll可能还包含了其他的dll,比如pcl_io_release这个dll,它就又引用了pcl_io_ply_release和OpenNI2这两个dll,如果没把这个dll也放进去的话,还是会报一样的错误的。笔者就是因为没把这两个dll也放进去,结果花了整整一天去找原因所在。
所以说,在封装带有第三方库的dll时,一定要把每一个dll的依赖都查看一遍,可能就是因为少一个dll,你的程序就会报错。
总结
在分发带有第三方库的dll时,切记把该dll依赖的其他dll一起分发,而且一定不要忘记,其他dll可能还引用了其他的dll,就像套娃一样,一层套一层。不管套了多少层,分发时一定要把所有的dll一起分发!