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
,用法類似。