1.JNA模擬結構體java
原生函數定義:函數
struct{ ipaddress struAddress;//結構體中包含結構體 device_register_cb fnCB;//回調函數 void *pUser; BYTE byRes[32]; }CmsListenParam, *CmsListenParam;
struct{ char szIP[128]; WORD wPort; BYTE byRes[2]; }ipaddress, *ipaddress;
java使用下面的方式模擬結構體:.net
public static class CmsListenParam extends Structure{ public IpAddress struAddress; public DeviceRegisterCB fnCB; public Pointer pUser; public byte[] byRes=new byte[32]; }
public static class IpAddress extends Structure{ public byte[] szIP=new byte[128]; public short wPort; public byte[] byRes=new byte[2]; }
JNA中,定義一個類型繼承Structure 類,用這個類來模擬C 語言的結構體。必須注意,Structure 子類中的公共字段的順序,必須與C 語言中的結構的順序一致。不然會報錯!由於,Java 調用動態連接庫中的C 函數,實際上就是一段內存做爲函數的參數傳遞給C函數。同時,C 語言的結構體是一個嚴格的規範,它定義了內存的次序。所以,JNA 中模擬的結構體的變量順序絕對不能錯。指針
當數據類型相同時,若交換位置,那麼程序不會報錯,可是數據會傳遞到錯誤的參數中。code
Structure 類表明了一個原生結構體。當Structure 對象做爲一個函數的參數或者返回值傳遞時,它表明結構體指針。當它被用在另外一個結構體內部做爲一個字段時,它表明結構體自己。另外,Structure 類有兩個內部接口Structure.ByReference 和Structure.ByValue。這兩個接口僅僅是標記,若是一個類實Structure.ByReference 接口,就表示這個類表明結構體指針。若是一個類實現Structure.ByValue 接口,就表示這個類表明結構體自己。用這兩個接口的實現類,能夠明肯定義咱們的Structure 實例表示的是結構體的指針仍是結構體自己。上面的例子中,因爲Structure 實例做爲函數的參數使用,所以是結構體指針。對象
因此可直接使用param對象做爲參數傳遞blog
CmsListenParam param=new CmsListenParam();
也可使用繼承
jCmsListenParam param=new CmsListenParam.ByReference();
明確指出param對象是結構體指針而不是結構體自己。接口
2.JNA模擬Unionip
原生代碼定義:
struct{ WORD wType; BYTE byRes[4]; union{ struct{ DWORD alarmNo; BYTE byRes[128]; }alarmRet; struct{ DWORD motNo; BYTE byRes[128]; }motionRet; struct{ DWORD chanNo; BYTE ruleID; }vcaRet; struct{ BYTE roomIndex; DWORD segmentNo; WORD segmetSize; }inquestRet; }seniorRet; }Search_Event_Ret,*Search_Event_Ret;
java使用JNA模擬Union
public static class NET_DVR_SEARCH_EVENT_RET extends Structure { public short wType; public byte[] byRes = new byte[4]; public static class SeniorRet extends Union { public static class AlarmRet extends Structure { public int alarmNo; public byte[] byRes = new byte[128]; } public static class MotionParam extends Structure { public int motRet; public byte[] byRes = new byte[128]; } public static class VcaRet extends Structure { public int chanNo; public byte ruleID; } public static class InquestRet extends Structure { public byte roomIndex; public int segmentNo; public short segmetSize; } } }
3.JNA模擬C回調函數
這裏只簡單介紹如何模擬回調函數,具體細節會在下一章節再詳細介紹
C回調函數定義
typedef BOOL (CALLBACK *device_register_cb)( LONG iUserID, DWORD dwDataType, void *pOutBuffer,//輸出參數緩衝區 DWORD dwOutLen,//輸出參數大小 void *pInBuffer,//輸入參數緩衝區 DWORD dwInLen,//輸入參數大小 void *pUser );
該回調函數的含義是,根據dwDataType的不一樣,pOutBuffer將指定不一樣的結構體對象,輸出不一樣的結果。pInBuffer用戶向回調函數中寫數據。
Java模擬回調函數實現:
public static interface DeviceRegisterCB extends StdCallCallback{ public boolean invoke(NativeLong iUserID,int dwDataType, DevRegInfo pOutBuffer,int dwOutLen, ServerInfo pInBuffer,int dwInLen,Pointer pUser); }
在C中 void *pOutBuffer表示能夠指向任何類型,在運行時動態肯定。所以在這裏使用具體類型DevRegInfo pOutBuffer模擬void *pOutBuffer。
定義DeviceRegisterCB實現類:
public static interface DeviceRegisterCBImpl extends StdCallCallback{ public boolean invoke(NativeLong iUserID,int dwDataType, DevRegInfo pOutBuffer,int dwOutLen, ServerInfo pInBuffer,int dwInLen,Pointer pUser){ System.out.println("數據類型:"+dwDataType); //...編寫具體的業務 return true; } }
到此爲止,整個NativeLong startListen(CmsListenParam listenPara)方法涉及的結構體,回調函數已經定義完成,下面將參數傳遞到java方法調用原生函數:
CmsListenParam param=new CmsListenParam(); byte [] ip =stringToByteArray("127.0.0.1",128);//字符串轉化爲byte,這裏長度對應結構體中參數的長度 System.arraycopy(pAlarmIP, 0, param.struIPAdress.szIP, 0, ip.length); param.struIPAdress.wPort =7200; param.fnMsgCb=new DeviceRegisterCBImpl(); param.pUserData=Pointer.NULL; param.byProtocolType=1; param.write(); //爲什麼調用該方法? CmsServer.instance.startListen(param);
public static byte[] stringToByteArray(String str,int len){ byte b[]=new byte[len]; for(int i=0;i<str.length();i++){ b[i]=Byte.parseByte((int)str.charAt(i)+""); } return b; }
爲什麼在將參數傳遞到方法時,要先調用param.write()?
Java 調用原生函數時,會把傳遞給原生函數的Java 數據固定在內存中,這樣原生函數才能夠訪問這些Java 數據。對於沒有固定住的Java 對象,GC 能夠刪除它,也能夠移動它在內存中的位置,以使堆上的內存連續。若是原生函數訪問沒有被固定住的Java 對象,就會致使調用失敗。Structure 類的write()方法會把結構體的全部字段固定住,使原生函數能夠訪問。
歡迎指出本文有誤的地方,轉載請註明原文出處https://my.oschina.net/7001/blog/672427