之前从来没搞过C++,最近被安排的任务需要调用C++的接口,对于一个没用过 Dependency 的小白来说,原本以为像平时的Http接口那样,协议,端口一定义,方法参数一写就没事,结果踩了无数的坑。现在从0基础开始记录。A发了一个SDK文件夹过来,先不管cpp、h、lib五花八门的后缀文件,直接看文档说明,表明需要调哪些方法。网上简单的查阅下资料,发现直接引用动态库Dll 的方式是通过 DllImport去实现,命名空间为 using System.Runtime.InteropServices;
按照文档中方法名、参数类型的说明,写下对应代码
[DllImport("ConX.dll", EntryPoint = "add")] public static extern int add(byte[] cfg_dir_name); //VS2017中 方法名首字母需要大写为 Add public void Test1() { int a=0; try { string str = "123"; byte[] bts = Encoding.Default.GetBytes(str); a = add(bts); } catch (Exception ex) { string strEx = ex.Message; } Console.WriteLine(a); Console.ReadLine(); }
一运行,提示 " 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)" ,这一般是版本不匹配的原因,确定dll 版本为64位后,将Debug中设置为 x64 即可 (32位则设置为x86),接着运行 ,会提示 " 找不到Dll的入口点 "
用 DepenDency 查看下该Dll ,发现所谓的函数名变化了,并不是add,变成了下图一长串的字符(CallingConvention.Cdecl 编码)
相关资料显示,在导出C++程序是,可以选择C方式,和C++方式,C方式不会变更函数名,C++则会, 项目右键--属性--配置属性--C/C++--高级--编译为--①C ②C++ ,当然还可以在原函数中的前面加上Extern C来限定导出后函数名不变,目前没有源码,只有dll,所以对于这种情况,只需要将变更的函数名作为入口点 赋值给 EntryPoint 即可
[DllImport("ConX.dll", EntryPoint = "?add@@YAHABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z")]
接着运行,这个时候 又会提示 " 请检查PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配 " , 资料显示,调用平台的默认约定 为Winapi , 而这种变更方法后的格式 " ?方法名@@YA-----------@Z " 为Cdecl 约定,于是再次更改导入dll的代码
[DllImport("ConX.dll", EntryPoint = "?add@@YAHABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z",CallingConvention=CallingConvention.Cdecl)]
如此折腾了一番,运行后, 如果报 " 无法加载 DLL“face_export.dll”: 找不到指定的程序 " 那就是路径有问题,一般处理方法如下
①直接把Dll 拷贝到 执行文件exe所在的目录下,记住通过Dependency 查看dll时,看下有没有缺失的dll ,如果没有,在将所依赖的非系统的dll 一并拷贝到 执行文件exe所在的目录下,
②使用 Dll 的绝对路径
③将 Dll 拷贝至系统的文件下,64位系统 对应的32位Dll则为SysWOW ,64为Dll 则为System32
正常情况下,到这里就没有什么大问题,如果报参数问题,那就需要查阅 C++ 对应的 C# 数据类型。匹配正确即可,关键就在于我这里仍然报错,外部组件异常,外部组件异常,外部组件异常,查阅资料显示,网上没什么好的解决方案,因为这种问题的原因五花八门,是一个很笼统的错误,可是偏偏就被我遇到了,我这边按接口中函数的申明,照葫芦画瓢写了一个C++ 的Dll,用C#调用自己写的Dll 是OK 。同事用C++调用 第三方的 Dll 也没问题(说明别人提供的Dll没问题)。这样一来就尴尬了,思路不能沉寂在这里。网上有人说是Net 版本可能不兼容,我挨个尝试一遍从3.5 切换到 4.6.1 仍是外部组件异常的错误。 最后我用VS2017 打开,调试程序,仍然是外部组件异常。现在可以排除的是 不是依赖问题(报找不到Dll),不是没加载指定Dll问题, 因为不会报入口点找不到的错误,也不是参数的问题,版本的问题已经用VS2017尝试过。还会有什么方面的原因呢?
未完待续...
最后经过翻来覆去的测试,发现问题出在 c++ 的dll上,最后重新封装了一边dll,再次调用就没问题了,看来还是自己学习的不够...