Java想访问C代码编译的动态库,一般通过JNA
方式实现,该方式好处是不用像JNI方式那样编写C wrapper代码,省去在Java工程里管理C代码的麻烦,但类型转换的坑却变多了。
最近在调试一款工业相机,它的主机接口有USB、GIGE、CameraLink等,SDK在枚举设备时会返回一个相机列表,每个表项都是一个相机,其类型按主机接口分类可能是上述3种任意一种,所以SDK一般都用联合体(Union)来表示,见下面结构体的SpecialInfo
字段。
typedef struct _MV_CC_DEVICE_INFO_
{
unsigned short nMajorVer;
unsigned short nMinorVer;
union
{
MV_GIGE_DEVICE_INFO stGigEInfo;
MV_USB3_DEVICE_INFO stUsb3VInfo;
MV_CamL_DEV_INFO stCamLInfo;
// more ...
}SpecialInfo;
}MV_CC_DEVICE_INFO;
应用程序想通过JNA读取nMajorVer字段,只需要cameraInfo.nMajorVer即可,但访问联合体却不像普通结构体那样简单,因为JNA的Union类JavaDoc里明确写着:
The current field is always unset by default to avoid accidentally attempting to read a field that is not valid.
即不告知JNA结构体的实际类型就读取的话,读到的永远是0
想读到有效值,需要
- cameraInfo.SpecialInfo.setType(MV_GIGE_DEVICE_INFO.class); // 设置这段内存实际存储的结构体类型
- cameraInfo.SpecialInfo.read(); // 按实际结构体类型
反序列化
这块内存段 - MV_GIGE_DEVICE_INFO gigeInfo = cameraInfo.SpecialInfo.stGigEInfo; // 按字段偏移读取结构体内的指定字段
如果只读取不写入,可以用一个快捷方法getTypedValue
,下面代码先用getTypedValue
提取结构体的内容,然后读取
MV_GIGE_DEVICE_INFO gigeInfo = (MV_GIGE_DEVICE_INFO)cameraInfo.SpecialInfo.getTypedValue(MV_GIGE_DEVICE_INFO.class);
byte[] rawCameraName = gigeInfo.chUserDefinedName;
相应的,如果只写入不读取,则用setTypedValue
,用法类似。