一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Java工作筆記:工作中使用JNA調(diào)用C++庫(kù)的一些細(xì)節(jié)

 QomoIT 2019-05-21

1.調(diào)用本地接口:

先看最基本的調(diào)用代碼:

  1. public interface testFunction extends Library{
  2. testFunction INSTANCE = (testFunction) Native.loadLibrary(Platform.isWindows() ? "win_sdk" : "linux_sdk", testFunction.class);
  3. int SDK_init();
  4. int SDK_Login(byte[] szUserLoginName, byte[] szPassword, String szIpAddress, SDKStructure.LOGIN_INFO_S pstSDKLoginInfo);
  5. }

調(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++的代碼:

  1. __declspec( dllexport ) ULONG_32 STDCALL SDK_Login
  2. (
  3. IN CHAR szUserLoginName[IMOS_NAME_LEN],
  4. IN CHAR szPassword[IMOS_PASSWD_ENCRYPT_LEN],
  5. IN CHAR szIpAddress[IMOS_IPADDR_LEN],
  6. OUT LOGIN_INFO_S *pstSDKLoginInfo
  7. );
是不是很奇怪,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)換: 
同樣先看代碼:

  1. /**
  2. * @struct tagLoginInfo
  3. * @brief 用戶登錄信息結(jié)構(gòu)體
  4. * @attention 無(wú) */
  5. public static class LOGIN_INFO_S extends Structure {
  6. public USER_LOGIN_ID_INFO_S stUserLoginIDInfo= new USER_LOGIN_ID_INFO_S();
  7. public byte[] szOrgCode = new byte[48];
  8. public byte[] szDomainName = new byte[64];
  9. public int ulDomainType;
  10. public LOGIN_INFO_S() {
  11. }
  12. @Override
  13. protected List<String> getFieldOrder() {
  14. return Arrays.asList(new String[]{"stUserLoginIDInfo","szOrgCode","szDomainName","ulDomainType",});
  15. }
  16. }

C++ 代碼:
  1. typedef struct tagLoginInfo
  2. {
  3. USER_LOGIN_ID_INFO_S stUserLoginIDInfo;
  4. CHAR szOrgCode[48];
  5. CHAR szDomainName[64];
  6. ULONG_32 ulDomainType;
  7. }LOGIN_INFO_S;

其中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ù)指針。
  1. __declspec( dllexport ) ULONG_32 STDCALL IMOS_RegCallBackPrcFunc
  2. (
  3. IN ULONG_32 LoginID,
  4. IN CALL_BACK_PROC_PF pfnCallBackProc
  5. );
這里不討論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接口: 
  1. interface CALL_BACK_PROC_PF extends Callback {
  2. void invoke(Pointer pParam);
  3. }
然后,繼承該接口寫(xiě)一個(gè)新類(lèi)來(lái)獲取推送過(guò)來(lái)的信息(以告警相關(guān)的信息舉例): 
  1. public static class AS_ALARMPUSH_UI_S extends Structure {
  2. public byte[] szAlarmSrcName = new byte[64];
  3. public byte[] szAlarmTime = new byte[32];
  4. public AS_ALARMPUSH_UI_S() {
  5. }
  6. @Override
  7. protected List<String> getFieldOrder() {
  8. return Arrays.asList(new String[]{"szAlarmSrcName","szAlarmTime"});
  9.   }
  10. }
最后調(diào)用:
  1. pfnCallBackProc = new SingleCallBackProcPF();
  2. ret = testFunction.INSTANCE.IMOS_RegCallBackPrcFunc(LoginID, pfnCallBackProc);
  3. if (0 != ret) {
  4. System.out.println(serverIP + ":" + serverPort + "]注冊(cè)推送信息處理的回調(diào)函數(shù)失敗,返回錯(cuò)誤碼:" + ret);
  5. return;
  6. }
靈活處理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ò)誤的。 
  1. LOGIN_INFO_S [] stu = new LOGIN_INFO_S [3];
  2. for(int i = 0; i < 3; i ++)
  3. {
  4. stu[i] = new LOGIN_INFO_S ();
這種方法是錯(cuò)誤的,因?yàn)橛眠@種方法new出來(lái)的內(nèi)存不一定是連續(xù)的,到了C++那邊就亂了。正確的方法是調(diào)用jna庫(kù)中structure類(lèi)的toArray方法: 
  1. LOGIN_INFO_S [] stu = new LOGIN_INFO_S [3];
  2. 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)換代碼: 

  1. /**
  2. * @apiNote 給結(jié)構(gòu)體中的字符串賦值
  3. */
  4. public static void setSdkBytes(byte[] dst, String content) {
  5. byte[] srcBytes = new byte[0];
  6. try {
  7. srcBytes = content.getBytes("utf-8");
  8. } catch (UnsupportedEncodingException e) {
  9. e.printStackTrace();
  10. }
  11. int size = Math.min(srcBytes.length, dst.length);
  12. System.arraycopy(srcBytes, 0, dst, 0, size == dst.length ? dst.length - 1 : size);
  13. }
  14. /**
  15. * @apiNote JSONArray轉(zhuǎn)byte[]
  16. *
  17. * JSONArray轉(zhuǎn)換為C代碼中Char[x][y]
  18. * 既JSONArray -> byte[x*y] -> Char[x][y]
  19. */
  20. public static void byte2Copy(byte[] dst, JSONArray array, int x, int y){
  21. if(array.size() < x){
  22. x = array.size();
  23. }
  24. int f = 0;
  25. StringBuilder sb = new StringBuilder();
  26. for (int i = 0; i < x; i++){
  27. sb.insert(f, array.getString(i));
  28. f = f + y;
  29. }
  30. StringUtils.setSdkBytes(dst, sb.toString());
  31. }







  


                                    

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    亚洲中文在线男人的天堂| 欧美一区二区三区性视频 | 国产亚洲精品久久99| 日本最新不卡免费一区二区| 中文字幕亚洲精品人妻| 中文字幕在线五月婷婷| av一区二区三区天堂| 日本加勒比系列在线播放| 久久免费精品拍拍一区二区| 欧美国产在线观看精品| 亚洲a级一区二区不卡| 高跟丝袜av在线一区二区三区| 最近日韩在线免费黄片| 日本乱论一区二区三区| 日本少妇中文字幕不卡视频| 儿媳妇的诱惑中文字幕| 亚洲av日韩一区二区三区四区| 国产人妻熟女高跟丝袜| 性欧美唯美尤物另类视频| 在线免费观看一二区视频| 九九热精彩视频在线免费| 日韩精品在线观看一区| 亚洲淫片一区二区三区| 精品久久久一区二区三| 精品国产亚洲av久一区二区三区| 久久这里只精品免费福利| 不卡一区二区高清视频| 国产欧美日产中文一区| 91欧美日韩一区人妻少妇| 正在播放玩弄漂亮少妇高潮| 久久精品一区二区少妇| 91后入中出内射在线| 经典欧美熟女激情综合网| 亚洲一区二区三区三区| 午夜精品在线视频一区| 日韩精品视频高清在线观看| 正在播放国产又粗又长| 尹人大香蕉中文在线播放| 欧洲自拍偷拍一区二区| 亚洲国产综合久久天堂| 国产午夜精品在线免费看|