使用JNA 调DLL 常见错误之一
错误信息如下
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x72ff2989, pid=1276, tid=4516
#
-- 底下这行注明所使用的 JRE, JVM 版本
# JRE version: 6.0_37-b06
# Java VM: Java HotSpot(TM) Client VM (20.12-b01 mixed mode windows-x86 )
-- 错误的位置 (来自于C)
# Problematic frame:
# C [MSVCP80.dll+0x2989]
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
底下一堆信息
--------------- T H R E A D ---------------
Current thread (0x021ea400): JavaThread "main" [_thread_in_native, id=4516, stack(0x00390000,0x003e0000)]
siginfo: ExceptionCode=0xc0000005, reading address 0x00000034
Registers:
EAX=0x00000008, EBX=0x730c775d, ECX=0x0000001c, EDX=0xed646bff
ESP=0x003deec8, EBP=0x003deedc, ESI=0x0000001c, EDI=0x004eaba8
EIP=0x72ff2989, EFLAGS=0x00010212
(…… 省略 N 行)
Register to memory mapping:
EAX=0x00000008 is an unknown value
EBX=0x730c775d is an unknown value
ECX=0x0000001c is an unknown value
EDX=0xed646bff is an unknown value
ESP=0x003deec8 is pointing into the stack for thread: 0x021ea400
EBP=0x003deedc is pointing into the stack for thread: 0x021ea400
ESI=0x0000001c is an unknown value
EDI=0x004eaba8 is an unknown value
(…… 再省略 N 行)
Stack: [0x00390000,0x003e0000], sp=0x003deec8, free space=315k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [MSVCP80.dll+0x2989] std::basic_string,std::allocator >::_Inside+0x1
这信息大概是说 由于C 那边的 MSVCP80.dll 某个位置引用了非法地址之类的(C 不熟,蛮猜)
联合调试了下,是由于字符串位数的原因:
C 接口定义了 char *HKInstNo 之类的参数,对应到 JAVA 里,应该是 byte[] 类型,并且末尾要加上 0x00 。
举个例子:
C 接口函数 : int GetInfo(char *id, carh *cl);
JNA 接口定义: public int GetInfo(byte[] id, byte[] class);
JAVA 调用:
int res = GetInfo("001".getBytes(), "101".getBytes());
此时JVM 就会退出,并会出现上面的错误信息。
必需给字符串加个终止符
========= 隔下 ================
写了个加终止符的方法
public byte[] String2byte(String str){
byte[] src = str.getBytes();
byte[] dest = new byte[src.length + 1];
System.arraycopy(src, 0, dest, 0, src.length);
dest[src.length] = 0x00; // 这句可以没有,因为 new 的时候已经初始化为 0
return dest;
}
然后把上面的JAVA调用改为
int res = GetInfo(String2byte("001"), String2byte("101"));
运行通过,能得到返回值。
=================== 一个类似的错误 ============================
结构体定义了byte[] 的长度,并且已经初始化
public static class ORDERINFO extends Structure {
public
byte
[]
orderCreator
=
new
byte
[32];
// 订单创建 人
public
byte
[]
outOrderNo
=
new
byte
[128];
// 外部订单号 56
public
byte
[]
orderType
=
new
byte
[5];
……………………
public
GOODSINFO.ByReference
goods
;
public
static
class
ByReference
extends
ORDERINFO
implements
Structure.ByReference{}
public
static
class
ByValue
extends
ORDERINFO
implements
Structure.ByValue{}
}
ORDERINFO.ByReference orderinfo = new ORDERINFO.ByReference();
orderinfo.orderCreator = Info.String2byte("zhouyy");
orderinfo.outOrderNo = Info.String2byte("0510030511355563");
orderinfo.orderType = Info.String2byte("222S");
………………
orderinfo.goods = goodsinfo;
业务中对公共字段进行赋值,公共字段所指向的空间已经改变, 因此 orderCreator 的长度已经不是 32位, 而是 zhouyy 6位
所以这边应该使用 数据拷贝 来复制数据, java 有内部方法用来拷贝数组:
System. arraycopy(src, 0, dest, 0, src.
length
);
ORDREINFO 结构体里有个指针 GoodsInfo , 在对 GoodsInfo 复制完数据之后要把 goodsinfo 固定 住内存,否则会因为内存溢出而导致 dll 蹦溃,反应到JAV 就是JVM 异常退出,并抛出类似上面的 错误信息。
goodsinfo.writer();