1.調(diào)用本地接口:
先看最基本的調(diào)用代碼:
public interface testFunction extends Library{ testFunction INSTANCE = (testFunction) Native.loadLibrary(Platform.isWindows() ? "win_sdk" : "linux_sdk", testFunction.class); int SDK_Login(byte[] szUserLoginName, byte[] szPassword, String szIpAddress, SDKStructure.LOGIN_INFO_S pstSDKLoginInfo);
調(diào)用C++ DLL庫(kù)的接口時(shí)一般繼承Library類(lèi)就可以了,但如果DLL文件是用stdcall編譯的話,DLL中的函數(shù)名變?yōu)?span style="font-family:simsun;line-height:21px;">“_functionname@number”這樣的形式,所以添加繼承這個(gè)類(lèi)會(huì)提示Error
looking up function 'SDK_init'。這時(shí)候就要繼承StdCallLibrary這個(gè)類(lèi)了。
可是!這里我想說(shuō),有時(shí)候 JNA就算繼承StdCallLibrary類(lèi)還是有可能提示找不到函數(shù)。我就遇到了,網(wǎng)上查了各種辦法都沒(méi)有解決(如果有人知道有效的解決辦法還請(qǐng)告知?。?。于是我要求公司提供C庫(kù)的程序員給我提供cdecl編譯的庫(kù),我也寫(xiě)過(guò)C++代碼,方法就是把cpp文件和h文件里面的__stdcall換成__cdecl。
當(dāng)然linux下就沒(méi)有出現(xiàn)過(guò)這樣的問(wèn)題,上面的例子中根據(jù)不同的操作系統(tǒng)調(diào)用不同名字的庫(kù)。
注意SDK_Login()這個(gè)函數(shù),下面是C++的代碼:
__declspec( dllexport ) ULONG_32 STDCALL SDK_Login IN CHAR szUserLoginName[IMOS_NAME_LEN], IN CHAR szPassword[IMOS_PASSWD_ENCRYPT_LEN], IN CHAR szIpAddress[IMOS_IPADDR_LEN], OUT LOGIN_INFO_S *pstSDKLoginInfo
是不是很奇怪,CHAR類(lèi)型的變量,在轉(zhuǎn)成java的時(shí)候,既有byte[]類(lèi)型也有String類(lèi)型?因?yàn)橹形木幋a,當(dāng)szUserLoginName變量為中文時(shí)(支持中文用戶名登錄),在傳輸過(guò)程中總會(huì)出現(xiàn)各種各樣的問(wèn)題,最后按照UTF8編碼轉(zhuǎn)換成byte[]類(lèi)型,再往下傳就沒(méi)什么問(wèn)題了。
2.結(jié)構(gòu)體和java間的轉(zhuǎn)換:
同樣先看代碼:
* @brief 用戶登錄信息結(jié)構(gòu)體 public static class LOGIN_INFO_S extends Structure { public USER_LOGIN_ID_INFO_S stUserLoginIDInfo= new USER_LOGIN_ID_INFO_S(); public byte[] szOrgCode = new byte[48]; public byte[] szDomainName = new byte[64]; protected List<String> getFieldOrder() { return Arrays.asList(new String[]{"stUserLoginIDInfo","szOrgCode","szDomainName","ulDomainType",});
C++ 代碼:
typedef struct tagLoginInfo USER_LOGIN_ID_INFO_S stUserLoginIDInfo;
其中g(shù)etFieldOrder()方法中的內(nèi)容必填完整,字符串?dāng)?shù)字中的值必須和結(jié)構(gòu)體中的成員名一一對(duì)應(yīng),成員必須是public。記住這三個(gè)必須,否則運(yùn)行時(shí)會(huì)拋異常。這個(gè)是在jna 4.2.2中新加的,在jna3.*中沒(méi)有強(qiáng)制要寫(xiě)這部分,但是在運(yùn)行中也會(huì)出現(xiàn)問(wèn)題,但是不會(huì)拋異常,然后一臉懵逼。。。
類(lèi)型轉(zhuǎn)換的時(shí)候,注意要把char換成byte[],聲明并且new好長(zhǎng)度。結(jié)構(gòu)體中有結(jié)構(gòu)體也要new好。為什么?留個(gè)念想,這個(gè)后面再說(shuō)。
3.回調(diào)推送信息處理
回調(diào)相對(duì)比較復(fù)雜一點(diǎn),先看C++代碼的頭文件中相關(guān)內(nèi)容:
typedef VOID (STDCALL *CALL_BACK_PROC_PF)( IN VOID *pParam); 聲明了一個(gè)函數(shù)指針。
__declspec( dllexport ) ULONG_32 STDCALL IMOS_RegCallBackPrcFunc IN CALL_BACK_PROC_PF pfnCallBackProc
這里不討論C++回調(diào)的實(shí)現(xiàn)。我們只知道,我們需要通過(guò)該函數(shù),獲取C那邊的推送消息就可以了??梢钥闯鲈摵瘮?shù)的入?yún)⒂幸粋€(gè)函數(shù)指針。那么這種指針是怎么在java中使用呢?
Java的調(diào)法:
首先,在testFunction接口中加一個(gè)內(nèi)部類(lèi)接口和C++中的CALL_BACK_PROC_PF對(duì)應(yīng)(入?yún)㈩?lèi)型使用jna自帶的Pointer類(lèi))該接口繼承jna庫(kù)中的CallBack接口:
interface CALL_BACK_PROC_PF extends Callback { void invoke(Pointer pParam);
然后,繼承該接口寫(xiě)一個(gè)新類(lèi)來(lái)獲取推送過(guò)來(lái)的信息(以告警相關(guān)的信息舉例):
public static class AS_ALARMPUSH_UI_S extends Structure { public byte[] szAlarmSrcName = new byte[64]; public byte[] szAlarmTime = new byte[32]; public AS_ALARMPUSH_UI_S() { protected List<String> getFieldOrder() { return Arrays.asList(new String[]{"szAlarmSrcName","szAlarmTime"});
最后調(diào)用:
pfnCallBackProc = new SingleCallBackProcPF(); ret = testFunction.INSTANCE.IMOS_RegCallBackPrcFunc(LoginID, pfnCallBackProc); System.out.println(serverIP + ":" + serverPort + "]注冊(cè)推送信息處理的回調(diào)函數(shù)失敗,返回錯(cuò)誤碼:" + ret);
靈活處理C++和Java之間的轉(zhuǎn)換:
Java和C++中都有用到指針的概念,只是Java對(duì)指針又重新封裝了一層,讓人基本感覺(jué)不到指針的存在。但是個(gè)人認(rèn)為對(duì)指針的進(jìn)一步理解,是對(duì)Java的學(xué)習(xí)很有幫助的,尤其是C++和Java之間轉(zhuǎn)換這一塊。
這就是之前為什么說(shuō)要把char換成byte[],聲明好長(zhǎng)度。因?yàn)橐3諧++和Java兩邊聲明結(jié)構(gòu)體的時(shí)候內(nèi)存大小相等,這樣每個(gè)成員變量的指針才能一一對(duì)應(yīng)。
當(dāng)遇到C++函數(shù)中的入?yún)⒊霈F(xiàn)結(jié)構(gòu)體數(shù)組,或者結(jié)構(gòu)體中出現(xiàn)結(jié)構(gòu)體數(shù)組的時(shí)候,使用常用的方法給數(shù)組中每一項(xiàng)new一下是錯(cuò)誤的。
LOGIN_INFO_S [] stu = new LOGIN_INFO_S [3]; for(int i = 0; i < 3; i ++) stu[i] = new LOGIN_INFO_S ();
這種方法是錯(cuò)誤的,因?yàn)橛眠@種方法new出來(lái)的內(nèi)存不一定是連續(xù)的,到了C++那邊就亂了。正確的方法是調(diào)用jna庫(kù)中structure類(lèi)的toArray方法:
LOGIN_INFO_S [] stu = new LOGIN_INFO_S [3]; stu = (LOGIN_INFO_S [])(new LOGIN_INFO_S()).toArray(3);
該方法申請(qǐng)了一段連續(xù)的內(nèi)存。
也同樣用指針的原理處理char[][]這種二維數(shù)組的情況,我在處理char[x][y]二維數(shù)組的時(shí)候,在Java方面的映射為byte[x*y]??吹竭@里明白了吧,只要值在數(shù)組中的位置放對(duì)了,這兩個(gè)類(lèi)型就沒(méi)什么區(qū)別了。
Java中再做好相應(yīng)的String到byte[]間的轉(zhuǎn)換,就萬(wàn)事OK了(兩種類(lèi)型轉(zhuǎn)換的時(shí)候記得加入中文編碼類(lèi)型哦,不然的話麻煩一連串?。?。附轉(zhuǎn)換代碼:
* @apiNote 給結(jié)構(gòu)體中的字符串賦值 public static void setSdkBytes(byte[] dst, String content) { byte[] srcBytes = new byte[0]; srcBytes = content.getBytes("utf-8"); } catch (UnsupportedEncodingException e) { int size = Math.min(srcBytes.length, dst.length); System.arraycopy(srcBytes, 0, dst, 0, size == dst.length ? dst.length - 1 : size); * @apiNote JSONArray轉(zhuǎn)byte[] * JSONArray轉(zhuǎn)換為C代碼中Char[x][y] * 既JSONArray -> byte[x*y] -> Char[x][y] public static void byte2Copy(byte[] dst, JSONArray array, int x, int y){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < x; i++){ sb.insert(f, array.getString(i)); StringUtils.setSdkBytes(dst, sb.toString());
|