Java 使用Jna 调用 C# dll文件踩到的坑
Java 使用Jna 调用 C# dll文件踩到的坑
背景: java需要调用一个GSmart_C_x64.dll中的PhyValDecode方法
传参是:
extern static PhyVal PhyValDecode(byte head, byte sensor, byte[] rawdata);
返回实体类为
public struct PhyVal
{
public float Temperature;
public float acc_X;
public float acc_Y;
public float acc_Z;
public float ang_X;
public float ang_Y;
public float ang_Z;
};
坑1
报错: Unsupported return type class com.valeo.jna.PhyVal$ByValue in function PhyValDecode
原因: 没有继承Structure, 没有重写
@Override
protected List getFieldOrder()
坑2
报错: invalid memory access
这个问题, 我看到有很多网友都发了, 但引发这个问题的有很多种原因
原因1: 返回参数的实体类没有实现Structure.ByValue 这个代码
// 必须定义此内部类,否则JNA可能无法正确实例化public static class ByValue extends PhyVal implements Structure.ByValue {}
那你实现了这个代码之后, 一定要记得引用
我在我的返回实体类出参interface是这样写的PhyVal.ByValue
public interface VTSSDK2 extends Library {VTSSDK2 INSTANCE = Native.load("VTS_GSmart_C_x64.dll", VTSSDK2.class);PhyVal.ByValue PhyValDecode(byte head,byte sensor, Pointer rawData);
}
坑3
报错: invalid memory access
这个是传参问题, 我上面说的是入参问题
C#的传参是
extern static PhyVal PhyValDecode(byte head, byte sensor, byte[] rawdata);
但我这样调用
PhyVal.ByValue PhyValDecode(byte head,byte sensor,byte[] rawData);
依旧报错invalid memory access, 后面问了AI , AI建议我使用Pointer这个类
import com.sun.jna.Pointer;
使用了这个类之后就OK了, 后面查询原因可能是因为
byte[] 导致内存溢出的原因
当 JNA 调用原生函数(如 DLL 中的函数)并传递 byte[] 时,会执行以下操作:自动分配原生内存:JNA 会在原生内存(非 JVM 堆内存)中分配一块与 byte[] 长度相同的缓冲区。
数据复制:将 Java 中 byte[] 的内容完整复制到这块原生内存中。
自动释放:函数调用结束后,JNA 会自动释放这块原生内存。如果出现内存溢出,通常是因为:数组过大:如果 byte[] 长度非常大(例如几 MB 甚至 GB),JNA 分配的原生内存块会占用大量原生内存,超过系统限制时就会溢出。
频繁分配:如果频繁调用该函数(例如在循环中),JNA 会频繁分配和释放原生内存,可能导致原生内存碎片或分配不及时,引发溢出。
函数对内存的特殊要求:如果原生函数期望的是一块预分配的、更大的内存(例如函数内部会向指针写入超过 byte[] 长度的数据),byte[] 的固定长度会导致写入越界,表现为内存溢出(本质是缓冲区溢出)。Pointer 不报错的原因
Pointer 是对原生内存地址的直接引用,不涉及自动内存分配和复制,具体行为如下:不自动分配内存:Pointer 本身只是一个地址标识,需要你手动分配原生内存(通常通过 new Memory(size) 实现,Memory 是 Pointer 的子类)。
无数据复制:如果 Pointer 指向的是你预先分配的 Memory,Java 数据写入 Memory 时是直接操作原生内存,无需额外复制。
灵活控制内存大小:你可以根据原生函数的实际需求,分配足够大的 Memory(例如 new Memory(1024 * 1024) 分配 1MB 内存),避免缓冲区溢出。
那这里我在写一个Point的使用示例:
Pointer dataPointer = new com.sun.jna.Memory(dataWithoutEnd.length);
dataPointer.write(0, dataWithoutEnd, 0, dataWithoutEnd.length);