我们的产品上有读卡操作,所以需要和一些第三方厂商的动态库做对接。
最近再给一个项目做升级,对接电子医保。
调用对方提供的动态库时,居然直接闪退,异常都catch不到,奇葩了。。。
后来经过调查,还是动态库导入时声明的方法签名错误导致的。
错误的代码
cs
[DllImport("SendRcv4.dll")]
public static extern string SendRcv4(string startFlag, string sendMsg, StringBuilder receivedMsg);
公司另外一个部门同事提供的代码,在.net framework 4.5及以上版本中调用直接闪退。
后来咨询了这位同事,说是要在.net framework 3.5版本调用才行。
果断写了个demo,将.net framework版本降级至4.0 调用果然就正常了。。。
但懊恼的是,我们的程序都是.net framework 4.6.2版本的,不可能为了一个第三方的类库调用,去降级啊,那成本可太大了。
经过网上查找,发现是由于入参和返回值的类型不对导致的 参考链接
正确的代码
cs
[DllImport("SendRcv4.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SendRcv4(IntPtr startFlag, IntPtr sendMsg, StringBuilder receivedMsg);
入参和返回值类型应该为 IntPtr
:用于表示指针或句柄的平台特定类型
通常与C++类库交互时,都应该使用指针类型
存在一个隐患那就是内存释放问题,可以在DllImport时,指定由被调用方进行内存释放CallingConvention = CallingConvention.StdCall
如果不好使的话,就需要我们手动来释放内存了。
cs
Marshal.FreeHGlobal(sendMsg);
调用代码示例
cs
var output = new StringBuilder(2048);
//将字符串转换为IntPtr类型
var startFlat = Marshal.StringToHGlobalAnsi("12345678");
var sendMsg = Marshal.StringToHGlobalAnsi("12345678");
SendRcv4(startFlat, sendMsg, output);
//释放内存
Marshal.FreeHGlobal(startFlat);
Marshal.FreeHGlobal(sendMsg);
期间还遇到一个异常:
"System.AccessViolationException"类型的未经处理的异常在 未知模块。 中发生尝试读取或写入受保护的内存。这通常指示其他内存已损坏 。
这是由于没有给接收返回数据的output变量指定初始值大小导致的
cs
//引发异常:System.AccessViolationException
var output = new StringBuilder();
//正常执行
var output = new StringBuilder(2048);
好了,到此为止。。。