目前主流的跨語言異構子產品通信方案有很多種,比如:
1、跨語言的RPC調用(Apache Thrift):它是Facebook貢獻給Apache基金會的開源項目,旨在建構跨語言平台的通信方案。目前它支援非常多種語言,其中當然包括C/C++和Java。Thrift内置一個語言編譯器,可以根據Thrift的文法規範,編譯生成指定語言的RPC調用子產品,功能也是非常的強大。Thrift的文法規範裡面定義了資料類型、資料子產品結構,有點類似WebService裡面的WSDL檔案。通過Thrift,我們就可以實作跨語言的異構子產品間的通信。
2、Google Protobuf:功能上支援C/C++和Java的語言綁定,基于對象消息的序列化、反序列化技術。性能好,效率高。proto檔案生成目智語言代碼,就可以實作跨語言的子產品通信。當然,還有一個和Google Protobuf類似的Apache開源項目Avro,也能達到這個目标。
3、FCGI服務方式:全稱為FastCGI,CGI的更新版本。支援HTTP協定對其進行通路,而CGI( Common Gateway Interface)通用網關接口,本身也隻是一種接口協定,它目前有支援C/C++語言的版本庫。值得注意的是:FCGI、CGI本身要通過Web伺服器進行消息轉發方式才能被調用。常見的Web伺服器有:Apache、Nginx等等。具體的調用過程是:Web伺服器接收到用戶端的請求之後,通過标準輸入流的方式把資料送給FCGI、CGI服務,FCGI、CGI服務計算、運作之後,把結果通過标準輸出流的方式傳回給用戶端,是以,理論上隻要能操作标準輸入流、标準輸出流的語言都支援FCGI、CGI服務方式實作。通過上面的分析,跨語言的調用也簡單了。即通過C/C++把主要的業務功能子產品,封裝成FCGI、CGI服務。然後Java利用HTTP協定,通過Web伺服器轉發到FCGI、CGI服務上,就能滿足跨語言子產品通信的要求。
當然,還有其它很多種跨語言的通信方式,在此,我就不一一舉例了。
由于本人工作的需要,要将目前在用的一些關鍵業務子產品,移植到Java平台下面。因為原來的大多數業務子產品是用C/C++語言實作的,而且涉及的邏輯比較複雜,直接遷移到Java平台下面,開發、遷移難度不小,而且還要涉及全量的功能性覆寫測試。對于開發、測試工作能力的要求比較高。那麼,除了上述歸納的跨語言通信方案之外,還有沒有一種便捷、靈活的方案,能無縫的銜接Java和C/C++兩種業務功能子產品呢?雖然,本人還未達到上述成熟開源跨語言通信架構設計者,高屋建瓴般的能力。但是,可以基于一些成熟開源架構的設計思路、以及功能子產品,完全可以打造一個,全新的跨語言異構子產品通信解決方案。下面,我就重點跟大家介紹一種,基于JNA(Java Native Access)技術和C/C++異構子產品通信的解決方案。
本文的靈感,主要來自于Thrift、BGCC(百度開源的Java/C++跨語言通信架構,網址為:http://bgcc.baidu.com/)中,對于跨語言生成的子產品,可以通過特定的文法規範進行定制編寫(當然,WebService裡面也有類似的做法,比如WSDL、IDL等等)。以及,通過一些總結歸納發現:利用JNA技術對于某些特定場景的C/C++函數子產品通路形式,可以抽象提取出,某種固定的代碼編寫政策,于是乎,誕生了本篇文章。
說到JNA,就不得不提起JNI(Java Native Interface)。JNI作為Java平台的組成部分,它允許Java代碼,和其它語言編寫的代碼子產品進行互動。JNI一開始就設計成為本地語言,尤其是為C/C++子產品通信所設計的。利用JNI,Java可以和本地環境中,已經編譯的代碼子產品進行互動。首先,Java可以通過JNI調用本地的C/C++子產品元件,另外一方面,本地的C/C++代碼子產品也可以調用Java代碼。
說完了JNI,繼續說一下JNA。JNA(Java Native Access)是一個開源的Java架構,它的官方網址是:https://jna.java.net/,最早是由SUN公司主導開發的,建立在JNI基礎之上的一個開源架構。它簡化了JNI開發中很多繁瑣的步驟,可以認為:JNA是JNI抽象層次更高的封裝。利用JNA,我們隻要在Java的接口中,描述目标C/C++的函數子產品和資料結構,JNA就會自動實作Java接口到C/C++函數子產品和資料結構的映射關系,簡化了開發的步驟,降低了JNI開發的難度。本文中涉及的jna.jar可以去上述官網下載下傳使用,下載下傳位址:https://java.net/projects/jna/downloads。
JNA技術通路C/C++異構子產品的方式
JNA是直接通過和C語言方式的API進行通信通路的。是以,如果你要利用JNA和C++通信通路的話,沒有問題,但是要把C++面向對象的子產品,再封裝出C風格的函數即可。綜上所述,利用JNA可以直接和C/C++子產品進行互動通信。
JNA通路C/C++異構子產品政策
最常見的情況可以看下面的圖例說明。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZwpmL1kDO4gTNyQDOtUzM0AzM4UTOwkTM1AjNxAjMtkTN3MTM58CX1AjNxAjMvwVO1czMxkzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.jpg)
1、情況A是最常見的情況:C語言的函數子產品是常見資料類型集合。這裡所謂的C語言常見資料類型(原生資料類型Primitive)是指:int、float、double、char*、int*等等原生資料類型、原生資料指針類型所構成的函數子產品。
2、情況B是另外一種情況:C的函數子產品涉及到一些資料模型結構,也就是C裡面的結構體類型struct。結構體是由純粹的C原生資料類型、原生資料指針類型構成的。本文中,由于要和C++子產品進行通信,是以JNA/C++通路子產品生成器,優先考慮一種,比較通用的生成政策:它的C函數的僞代碼,表示為:void function(struct* pInArray,struct* pOutArray);其中pInArray表示待處理的結構體數組,而pOutArray表示處理後的結構體數組。
3、情況C是指:結構體類型struct裡面嵌套其它的結構體,組成複合資料類型的情況。
4、情況D是指:除了情況A、B、C之外的其它種情況。不過很多種情況,都可以間接通過情況B,進行适配轉換。是以,本文的例子,也是基于情況B,進行開發設計的。
JNA/C++通路子產品生成器設計思路
先來看下,JNA/C++通路子產品生成器設計思路的腦圖:
1、支援C/C++資料對象配置可定制化
由于是基于上述JNA通路C/C++異構子產品政策B的情況。是以要對通路的子產品結構類型進行描述。對應的描述檔案名為JNA.xml,該檔案内容描述如下:
<jna>
<package>Java通路子產品的包名</package>
<module>子產品名稱</module>
<struct>
<attr>
<name>子產品屬性名稱</name>
<type>子產品屬性類型</type>
</attr>
<attr>
<name>子產品屬性名稱</name>
<type>子產品屬性類型</type>
</attr>
</struct>
</jna>
其中,package節點表示對應生成Java通路子產品的包名;module表示要通路的子產品名稱;struct節點下面,包含若幹attr節點。attr節點的name表示子產品某個屬性的名稱;attr節點的type表示子產品某個屬性的類型(目前隻支援整型int/字元型string),上述JNA.xml對應的類圖結構為:
其中,JNAModule結構體(JNA子產品屬性)是和JNA.xml配置檔案的内容一一對應。另外一個JNAConfig子產品(屬性配置管理)是負責解析JNA.xml子產品資訊配置檔案,生成對應的JNA通路子產品代碼和C++通路子產品代碼。
2、JNA通路C/C++異構子產品政策可配置
目前設計的JNA通路C/C++異構子產品政策隻支援情況B,是以在JNA.xml配置檔案中,沒有展現子產品政策規則屬性,後續可以擴充完善。
3、自動生成JNA通路子產品代碼
對應的類子產品為:void JNAConfig::genJNAModule(JNAModule& module)中的JNA c++ 檔案頭子產品定義、JNA c++ 檔案實作子產品定義部分。
4、自動生成C/C++通路子產品代碼
對應的類子產品為:void JNAConfig::genJNAModule(JNAModule& module)中的JNA java 子產品定義子產品定義部分。
現在給出完整的代碼實作,首先是JNA/C++通路子產品生成器的頭檔案(C++實作)JNA.h
/**
* @filename:JNA.h
*
* Newland Co. Ltd. All rights reserved.
*
* @Description:JNA接口生成子產品定義
* @author tangjie
* @version 1.0
*
*/
#ifndef __JNA_H__
#define __JNA_H__
#ifdef WIN32
#include <Windows.h>
#include <direct.h>
#endif
#include <string>
#include <deque>
#include <iostream>
#include <algorithm>
using namespace std;
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/typeof/std/utility.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem/fstream.hpp>
using namespace boost;
//JNA 字段名稱以及字段類型,目前類型暫支援int/string
struct JNAAttr
{
string attrName;
int attrType;
enum AttrType
{
INT,
STRING
};
int getAttrType() const
{
return attrType;
}
};
//JNA 子產品屬性包含子產品名稱/包名。聚合若幹JNAAttr。
struct JNAModule
{
string packageName;
string moduleName;
deque<JNAAttr> lstAttr;
};
//JNA 屬性配置管理
class JNAConfig
{
public:
JNAConfig();
bool load(JNAModule& module);
void genJNAModule(JNAModule& module);
private:
string getConfigFilePath();
string getConfigFile();
};
#endif // (!__JNA_H__)
其次是JNA/C++通路子產品生成器的源檔案(C++實作):JNA.cpp
/**
* @filename:JNA.cpp
*
* Newland Co. Ltd. All rights reserved.
*
* @Description:JNA接口生成子產品實作
* @author tangjie
* @version 1.0
*
*/
#include "stdafx.h"
#include "JNA.h"
//JNA子產品實作部分
JNAConfig::JNAConfig(){}
void JNAConfig::genJNAModule(JNAModule& module)
{
string moduleName = module.moduleName;
if(moduleName.empty()) return;
//JNA c++ 檔案頭子產品定義
{
string macro = to_upper_copy(module.moduleName);
boost::filesystem::path p(".\\"+moduleName+"Processor.h");
boost::filesystem::ofstream ofs(p.string().c_str());
ofs<<"#ifndef __"<<macro<<"PROCESSOR_H__"<<endl;
ofs<<"#define __"<<macro<<"PROCESSOR_H__"<<endl<<endl;
ofs<<"#include <deque>\n#include <algorithm>\n#include <iterator>\nusing namespace std;"<<endl<<endl;
ofs<<"typedef struct "<<moduleName<<" {"<<endl;
BOOST_FOREACH(JNAAttr attr,module.lstAttr)
{
ofs<<((attr.attrType == JNAAttr::INT) ? " int ":" char* ")<<attr.attrName<<";"<<endl;
}
ofs<<"} "<<moduleName<<";"<<endl<<endl<<endl<<"#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n";
ofs<<" class "<<moduleName<<"Processor {\n public:\n "<<moduleName<<"Processor();\n void calcRule(deque<"<<moduleName<<"> input);\n static int getResultSize(){return output.size();}\n static deque<"<<moduleName<<"> getResult(){return output;}\n\n protected:\n static deque<"<<moduleName<<"> output;\n };\n\n //C Stype API for JNA invoke\n void "<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor("<<moduleName<<"* p"<<moduleName<<","<<moduleName<<"** pp"<<moduleName<<",int num"<<moduleName<<");\n"<<endl;
ofs<<" //because use malloc/free c-api otherwise lead to memory leaks\n void freeMemory("<<moduleName<<"* p"<<moduleName<<");\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // (!__"<<macro<<"PROCESSOR_H__)\n\n\n"<<endl;
ofs.close();
}
//JNA c++ 檔案實作子產品定義
{
bool existTypeString = false;
deque<JNAAttr>::iterator iter = find_if(module.lstAttr.begin(),module.lstAttr.end(),boost::bind<bool>(std::equal_to<int>(),JNAAttr::STRING,boost::bind(&JNAAttr::getAttrType, _1)));
if(iter != module.lstAttr.end())
{
existTypeString = true;
}
boost::filesystem::path p(".\\"+moduleName+"Processor.cpp");
boost::filesystem::ofstream ofs(p.string().c_str());
ofs<<"#include \""<<moduleName<<"Processor.h\"\n"<<endl;
ofs<<"deque<"<<moduleName<<"> "<<moduleName<<"Processor::output;\n"<<endl;
ofs<<moduleName<<"Processor::"<<moduleName<<"Processor()\n{\n "<<moduleName<<"Processor::output.clear();\n}\n"<<endl;
ofs<<"void "<<moduleName<<"Processor::calcRule(deque<"<<moduleName<<"> input)\n{\n if(input.empty()) return;\n\n copy(input.begin(),input.end(),std::back_inserter(output));\n}\n"<<endl;
ofs<<"void "<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor("<<moduleName<<"* p"<<moduleName<<","<<moduleName<<"** pp"<<moduleName<<",int num"<<moduleName<<")\n{\n deque<"<<moduleName<<"> list(p"<<moduleName<<",p"<<moduleName<<"+num"<<moduleName<<");\n\n "<<moduleName<<"Processor processor;\n\n processor.calcRule(list);\n\n deque<"<<moduleName<<"> result = "<<moduleName<<"Processor::getResult();\n int number = result.size();\n\n *pp"<<moduleName<<" = ("<<moduleName<<"*)malloc(sizeof("<<moduleName<<") * number);\n\n memset(*pp"<<moduleName<<", 0, sizeof("<<moduleName<<") * number);\n\n for(int i = 0;i<number;i++)\n {"<<endl;
BOOST_FOREACH(JNAAttr attr,module.lstAttr)
{
ofs<<((attr.attrType == JNAAttr::INT) ? (" (*pp"+moduleName+")[i]."+attr.attrName+" = result[i]."+attr.attrName+";\n"+" (*pp"+moduleName+")[i]."+attr.attrName+" = result[i]."+attr.attrName+";\n"):(" int "+attr.attrName+"Len = strlen(result[i]."+attr.attrName+") + 1;\n (*pp"+moduleName+")[i]."+attr.attrName+" = (char*)malloc(sizeof(char) * "+attr.attrName+"Len);\n strcpy((*pp"+moduleName+")[i]."+attr.attrName+", result[i]."+attr.attrName+");\n"))<<endl;
}
ofs<<" }\n\n return;\n}\n"<<endl;
if(existTypeString)
{
ofs<<"void freeMemory("<<moduleName<<"* p"<<moduleName<<")\n{\n deque<"<<moduleName<<"> result = "<<moduleName<<"Processor::getResult();\n"<<endl;
ofs<<" for(int i = 0;i<result.size();i++)\n {"<<endl;
BOOST_FOREACH(JNAAttr attr,module.lstAttr)
{
if(attr.attrType == JNAAttr::STRING)
{
ofs<<" free(result[i]."<<attr.attrName<<");"<<endl;
}
}
ofs<<" }\n"<<endl;
}
else
{
ofs<<"void freeMemory("<<moduleName<<"* p"<<moduleName<<")\n{"<<endl;
}
ofs<<" free(p"<<moduleName<<");\n}\n\n"<<endl;
ofs.close();
}
//JNA java 子產品定義
{
boost::filesystem::path p(".\\"+moduleName+"Processor.java");
boost::filesystem::ofstream ofs(p.string().c_str());
ofs<<"package "<<module.packageName<<";\n"<<endl;
ofs<<"import com.sun.jna.Library;\nimport com.sun.jna.Native;\nimport com.sun.jna.Pointer;\nimport com.sun.jna.Structure;\nimport com.sun.jna.Structure.ByReference;\nimport com.sun.jna.ptr.PointerByReference;\n\nimport java.util.ArrayList;\n\n"<<endl;
ofs<<"public class "<<moduleName<<"Processor {\n public interface JNALibrary extends Library {\n public static class "<<moduleName<<"Struct extends Structure {\n public static class ByReference extends "<<moduleName<<"Struct implements\n Structure.ByReference {\n }\n"<<endl;
BOOST_FOREACH(JNAAttr attr,module.lstAttr)
{
ofs<<" public "<<(attr.attrType == JNAAttr::INT ? "int " :"String ")<<attr.attrName<<";"<<endl;
}
ofs<<" public "<<moduleName<<"Struct() {\n }\n\n public "<<moduleName<<"Struct(Pointer p) {\n super(p);\n }\n }\n\n public void "<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor("<<moduleName<<"Struct.ByReference vals,\n PointerByReference valsRef, int numVals);\n\n public void freeMemory(Pointer p);\n }\n\n public static void invoke(ArrayList<JNALibrary."<<moduleName<<"Struct> list,\n ArrayList<JNALibrary."<<moduleName<<"Struct> listResult) {\n JNALibrary library;\n JNALibrary."<<moduleName<<"Struct.ByReference ref"<<moduleName<<" = null;\n\n PointerByReference refPtrVal;\n\n try {\n library = (JNALibrary) Native.loadLibrary(\n \"../src/lib"<<moduleName<<"Processor.so\", JNALibrary.class);\n ref"<<moduleName<<" = new JNALibrary."<<moduleName<<"Struct.ByReference();\n refPtrVal = new PointerByReference();\n } catch (UnsatisfiedLinkError e) {\n System.out.println(\"JNA invoke error\");\n library = (JNALibrary) Native.loadLibrary(\n \"./lib"<<moduleName<<"Processor.so\", JNALibrary.class);\n ref"<<moduleName<<" = new JNALibrary."<<moduleName<<"Struct.ByReference();\n refPtrVal = new PointerByReference();\n }\n\n JNALibrary."<<moduleName<<"Struct[] array = (JNALibrary."<<moduleName<<"Struct[]) ref"<<moduleName<<"\n .toArray(list.size());\n"<<endl;
ofs<<" for (int i = 0; i < list.size(); i++) {"<<endl;
BOOST_FOREACH(JNAAttr attr,module.lstAttr)
{
ofs<<" array[i]."<<attr.attrName<<" = list.get(i)."<<attr.attrName<<";"<<endl;
}
ofs<<" }\n"<<endl;
ofs<<" library."<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor(ref"<<moduleName<<", refPtrVal, array.length);\n\n Pointer pValues = refPtrVal.getValue();\n\n JNALibrary."<<moduleName<<"Struct refValues = new JNALibrary."<<moduleName<<"Struct(\n pValues);\n\n refValues.read();\n\n JNALibrary."<<moduleName<<"Struct[] arrayResult = (JNALibrary."<<moduleName<<"Struct[]) refValues\n .toArray(list.size());\n\n for (JNALibrary."<<moduleName<<"Struct element : arrayResult) {\n listResult.add(element);\n }\n\n library.freeMemory(pValues);\n }\n}\n"<<endl;
ofs.close();
}
}
bool JNAConfig::load(JNAModule& module)
{
boost::property_tree::ptree pt;
boost::property_tree::read_xml(getConfigFile().c_str(), pt);
try
{
module.packageName = pt.get<string>("jna.package");
module.moduleName = pt.get<string>("jna.module");
//加載解析JNA子產品字段定義
{
BOOST_AUTO(child, pt.get_child("jna.struct"));
for (BOOST_AUTO(pos, child.begin()); pos != child.end(); ++pos)
{
BOOST_AUTO(child_paths, pos->second.get_child(""));
JNAAttr attr;
int i = 0;
for (BOOST_AUTO(pos_paths, child_paths.begin()); pos_paths != child_paths.end(); ++pos_paths,++i)
{
if(i%2 == 0)
{
attr.attrName = pos_paths->second.data();
}
else
{
attr.attrType = to_lower_copy(string(pos_paths->second.data())).compare("string") ? JNAAttr::INT : JNAAttr::STRING;
}
}
module.lstAttr.push_back(attr);
}
}
return true;
}
catch(const std::exception& e)
{
fprintf(stderr, "%s\n", e.what());
return false;
}
catch(...)
{
fprintf(stderr, "Unexpected exception.\n");
return false;
}
}
//擷取配置檔案路徑資訊
string JNAConfig::getConfigFile()
{
return getConfigFilePath().append(".xml");
}
string JNAConfig::getConfigFilePath()
{
string result;
char path_buffer[_MAX_PATH + 1] = {0};
char drive[_MAX_DRIVE + 1] = {0};
char dir[_MAX_DIR + 1] = {0};
char fname[_MAX_FNAME + 1] = {0};
char ext[_MAX_EXT + 1] = {0};
::GetModuleFileNameA(NULL, path_buffer, sizeof(path_buffer) - 1);
_splitpath(path_buffer, drive, dir, fname, ext);
result += drive;
result += dir;
result += fname;
return result;
}
通過VS2010,編譯生成目标應用程式JNA.exe,然後把定義好的JNA.xml子產品描述檔案,放在JNA.exe的同一個目錄下面。比如,我要通過JNA通路C++子產品名稱為MobileUser(手機使用者),對應的Java通路子產品的包名:newlandframework.jna.businessrule。子產品屬性為:手機使用者歸屬地市homeCity、手機使用者網齡(即手機号碼使用年限)netAge,類型為整型int;手機号碼msisdn、使用者姓名name,類型為字元型string。綜上得到如下的子產品描述:
<jna>
<package>newlandframework.jna.businessrule</package>
<module>MobileUser</module>
<struct>
<attr>
<name>homeCity</name>
<type>int</type>
</attr>
<attr>
<name>msisdn</name>
<type>string</type>
</attr>
<attr>
<name>name</name>
<type>string</type>
</attr>
<attr>
<name>netAge</name>
<type>int</type>
</attr>
</struct>
</jna>
輕按兩下運作,自動生成對應的C/C++通路子產品代碼(MobileUserProcessor.h/cpp)、JNA通路子產品代碼(MobileUserProcessor.java),截圖如下:
生成代碼内容如下所列舉:
MobileUserProcessor.h檔案内容如下:
#ifndef __MOBILEUSERPROCESSOR_H__
#define __MOBILEUSERPROCESSOR_H__
#include <deque>
#include <algorithm>
#include <iterator>
using namespace std;
typedef struct MobileUser {
int homeCity;
char* msisdn;
char* name;
int netAge;
} MobileUser;
#ifdef __cplusplus
extern "C" {
#endif
class MobileUserProcessor {
public:
MobileUserProcessor();
void calcRule(deque<MobileUser> input);
static int getResultSize(){return output.size();}
static deque<MobileUser> getResult(){return output;}
protected:
static deque<MobileUser> output;
};
//C Stype API for JNA invoke
void mobileUserProcessor(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser);
//because use malloc/free c-api otherwise lead to memory leaks
void freeMemory(MobileUser* pMobileUser);
#ifdef __cplusplus
}
#endif
#endif // (!__MOBILEUSERPROCESSOR_H__)
MobileUserProcessor.cpp檔案内容如下:
#include "MobileUserProcessor.h"
deque<MobileUser> MobileUserProcessor::output;
MobileUserProcessor::MobileUserProcessor()
{
MobileUserProcessor::output.clear();
}
void MobileUserProcessor::calcRule(deque<MobileUser> input)
{
if(input.empty()) return;
copy(input.begin(),input.end(),std::back_inserter(output));
}
void mobileUserProcessor(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser)
{
deque<MobileUser> list(pMobileUser,pMobileUser+numMobileUser);
MobileUserProcessor processor;
processor.calcRule(list);
deque<MobileUser> result = MobileUserProcessor::getResult();
int number = result.size();
*ppMobileUser = (MobileUser*)malloc(sizeof(MobileUser) * number);
memset(*ppMobileUser, 0, sizeof(MobileUser) * number);
for(int i = 0;i<number;i++)
{
(*ppMobileUser)[i].homeCity = result[i].homeCity;
(*ppMobileUser)[i].homeCity = result[i].homeCity;
int msisdnLen = strlen(result[i].msisdn) + 1;
(*ppMobileUser)[i].msisdn = (char*)malloc(sizeof(char) * msisdnLen);
strcpy((*ppMobileUser)[i].msisdn, result[i].msisdn);
int nameLen = strlen(result[i].name) + 1;
(*ppMobileUser)[i].name = (char*)malloc(sizeof(char) * nameLen);
strcpy((*ppMobileUser)[i].name, result[i].name);
(*ppMobileUser)[i].netAge = result[i].netAge;
(*ppMobileUser)[i].netAge = result[i].netAge;
}
return;
}
void freeMemory(MobileUser* pMobileUser)
{
deque<MobileUser> result = MobileUserProcessor::getResult();
for(int i = 0;i<result.size();i++)
{
free(result[i].msisdn);
free(result[i].name);
}
free(pMobileUser);
}
MobileUserProcessor.java檔案内容如下:
package newlandframework.jna.businessrule;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.ByReference;
import com.sun.jna.ptr.PointerByReference;
import java.util.ArrayList;
public class MobileUserProcessor {
public interface JNALibrary extends Library {
public static class MobileUserStruct extends Structure {
public static class ByReference extends MobileUserStruct implements
Structure.ByReference {
}
public int homeCity;
public String msisdn;
public String name;
public int netAge;
public MobileUserStruct() {
}
public MobileUserStruct(Pointer p) {
super(p);
}
}
public void mobileUserProcessor(MobileUserStruct.ByReference vals,
PointerByReference valsRef, int numVals);
public void freeMemory(Pointer p);
}
public static void invoke(ArrayList<JNALibrary.MobileUserStruct> list,
ArrayList<JNALibrary.MobileUserStruct> listResult) {
JNALibrary library;
JNALibrary.MobileUserStruct.ByReference refMobileUser = null;
PointerByReference refPtrVal;
try {
library = (JNALibrary) Native.loadLibrary(
"../src/libMobileUserProcessor.so", JNALibrary.class);
refMobileUser = new JNALibrary.MobileUserStruct.ByReference();
refPtrVal = new PointerByReference();
} catch (UnsatisfiedLinkError e) {
System.out.println("JNA invoke error");
library = (JNALibrary) Native.loadLibrary(
"./libMobileUserProcessor.so", JNALibrary.class);
refMobileUser = new JNALibrary.MobileUserStruct.ByReference();
refPtrVal = new PointerByReference();
}
JNALibrary.MobileUserStruct[] array = (JNALibrary.MobileUserStruct[]) refMobileUser
.toArray(list.size());
for (int i = 0; i < list.size(); i++) {
array[i].homeCity = list.get(i).homeCity;
array[i].msisdn = list.get(i).msisdn;
array[i].name = list.get(i).name;
array[i].netAge = list.get(i).netAge;
}
library.mobileUserProcessor(refMobileUser, refPtrVal, array.length);
Pointer pValues = refPtrVal.getValue();
JNALibrary.MobileUserStruct refValues = new JNALibrary.MobileUserStruct(
pValues);
refValues.read();
JNALibrary.MobileUserStruct[] arrayResult = (JNALibrary.MobileUserStruct[]) refValues
.toArray(list.size());
for (JNALibrary.MobileUserStruct element : arrayResult) {
listResult.add(element);
}
library.freeMemory(pValues);
}
}
其中C函數void mobileUserProcessor(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser);中的參數描述如下:
MobileUser* pMobileUser表示待處理的結構子產品數組(一維指針);MobileUser** ppMobileUser表示處理後的結構子產品數組(二維指針);int numMobileUser表示待處理的結構子產品數組的大小。
利用JNA.exe(JNA/C++通路子產品生成器)生成代碼實作Java/C++通信通路
要實作Java和C++的跨語言通信通路,我們先來看下操作步驟:
現在我們根據上面的步驟一步一步來說明:
首先,我們完成一個簡單的功能:通過Java通路子產品MobileUser,Java傳入待處理的MobileUser數組集合,然後C++直接簡單地,把待處理資料直接拷貝傳回(當然,你可以在生成代碼模闆的基礎上,進行二次開發定制。),最後Java的用戶端列印出C++傳回的集合内容。
1、編輯JNA.xml定義通信子產品資訊
配置JNA.xml子產品資訊,MobileUser結構屬性内容如下:
<jna>
<package>newlandframework.jna.businessrule</package>
<module>MobileUser</module>
<struct>
<attr>
<name>homeCity</name>
<type>int</type>
</attr>
<attr>
<name>msisdn</name>
<type>string</type>
</attr>
<attr>
<name>name</name>
<type>string</type>
</attr>
<attr>
<name>netAge</name>
<type>int</type>
</attr>
</struct>
</jna>
2、運作JNA.exe (JNA/C++通路子產品生成器)
把JNA.exe和JNA.xml放在同一目錄下面,輕按兩下JNA.exe生成對應的JNA通路子產品、C/C++通路子產品。
3、根據政策生成JNA通路子產品
即JNA.exe生成的MobileUserProcessor.java代碼子產品。
4、根據政策生成C/C++通路子產品
即JNA.exe生成的MobileUserProcessor.h/cpp代碼子產品。
5、編譯C/C++通路子產品并生成對應動态庫
編譯MobileUserProcessor.h/cpp代碼子產品得到動态庫libMobileUserProcessor.so,Makefile參考如下:
GEN_SRC = $(shell ls .*.cpp)
rule:MobileUserProcessor.cpp
g++ -I../inc -Wall -Wextra -pedantic -g -O2 -std=c++98 -MD -MP -c -o MobileUserProcessor.o ./MobileUserProcessor.cpp;
g++ -shared -o libMobileUserProcessor.so MobileUserProcessor.o;
6、編譯JNA子產品生成.class檔案
編譯MobileUserProcessor.java代碼子產品,得到.class位元組碼檔案,參考指令如下:javac -classpath jna.jar -d . MobileUserProcessor.java ProcessMain.java。
7、打包JNA子產品.class檔案以及動态庫
I、先到https://java.net/projects/jna/downloads 的官網下載下傳jna.jar,然後把它解壓出來。解壓指令參考:jar xvf jna.jar com/
II、編寫調用用戶端ProcessMain.java,傳入待處理的資料集合一共5個,手機使用者名稱分别為Jim、Tom、John、Dave、William,給JNA裡面的C++子產品MobileUserProcessor.invoke。并列印C++的處理結果。現在給出調用用戶端ProcessMain.java的實作:
package newlandframework.jna.businessrule;
import java.util.ArrayList;
import newlandframework.jna.businessrule.MobileUserProcessor.JNALibrary;
public class ProcessMain {
public ProcessMain() {
}
public static void main(String[] args) {
ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> list = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>();
ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> listResult = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>();
MobileUserProcessor.JNALibrary.MobileUserStruct userA = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userB = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userC = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userD = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userE = new MobileUserProcessor.JNALibrary.MobileUserStruct();
userA.homeCity = 591;
userA.msisdn = "5911000";
userA.name = "Jim";
userA.netAge = 2;
list.add(userA);
userB.homeCity = 592;
userB.msisdn = "5921000";
userB.name = "Tom";
userB.netAge = 4;
list.add(userB);
userC.homeCity = 593;
userC.msisdn = "5931000";
userC.name = "John";
userC.netAge = 5;
list.add(userC);
userD.homeCity = 594;
userD.msisdn = "5941000";
userD.name = "Dave";
userD.netAge = 5;
list.add(userD);
userE.homeCity = 595;
userE.msisdn = "5951000";
userE.name = "William";
userE.netAge = 3;
list.add(userE);
MobileUserProcessor.invoke(list, listResult);
for(int i = 0; i < listResult.size(); i++) {
System.out.println(listResult.get(i).homeCity);
System.out.println(listResult.get(i).msisdn);
System.out.println(listResult.get(i).name);
System.out.println(listResult.get(i).netAge);
System.out.println("----------------------------------------------");
}
}
}
III、編寫MANIFEST.MF檔案,檔案内容參考如下:
Manifest-Version: 1.0
Class-Path: .
Main-Class: newlandframework.jna.businessrule.ProcessMain
Name: com/sun/jna/
Specification-Title: Java Native Access (JNA)
Implementation-Title: com.sun.jna
Implementation-Version: 3.3.0
Specification-Version: 3
Implementation-Vendor: JNA Development Team
Specification-Vendor: JNA Development Team
IV、然後把C++子產品的動态庫libMobileUserProcessor.so、MobileUserProcessor.java、ProcessMain.java代碼子產品編譯出的位元組碼檔案、jna.jar的位元組碼檔案、MANIFEST.MF打包生成MobileUserProcessor.jar。指令參考如下:jar cvfm MobileUserProcessor.jar ./META-INF/MANIFEST.MF ./newlandframework/jna/businessrule/MobileUserProcessor.class ./newlandframework/jna/businessrule/ProcessMain.class com ../src/libMobileUserProcessor.so。
8、運作jar包完成跨語言通信通路
在終端輸入:java -jar MobileUserProcessor.jar,然後運作截圖如下:
Java程式中,送入待處理的手機使用者名稱:Jim、Tom、John、Dave、William,通過JNA通路C++的處理子產品。正如我們預計的一樣,正确列印出了處理結果。
基于JNA.exe(JNA/C++通路子產品生成器)生成代碼的二次開發
現在我們更進一步,在原來生成的JNA/C++代碼上面進行二次開發。現在業務算法要求:通過JNA調用C++的處理子產品,傳入MobileUser數組集合,計算出集合中網齡最大和最小的前N個排名的記錄,然後,把排名結果傳回,Java列印出排序結果。
首先,還是通過JNA.exe生成對應的JNA/C++代碼子產品,然後進行二次開發,根據上述的業務要求,加入處理邏輯,封裝出C風格的處理子產品:
找出手機使用者MobileUser網齡最小的前topN個資料:
void minTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN);
找出手機使用者MobileUser網齡最大的前topN個資料:
void maxTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN);
然後對應的代碼内容如下:
MobileUserProcessor.h(C++)代碼内容如下:
/**
* @filename:MobileUserProcessor.h
*
* Newland Co. Ltd. All rights reserved.
*
* @Description:手機使用者網齡TOPN排名管理類定義
* @author tangjie
* @version 1.0
*
*/
#ifndef __MOBILEUSERPROCESSOR_H__
#define __MOBILEUSERPROCESSOR_H__
#include <string>
#include <iostream>
#include <deque>
#include <algorithm>
#include <functional>
#include <iterator>
#include <memory.h>
using namespace std;
//JNA異構子產品定義
typedef struct MobileUser {
int homeCity;//歸屬地市
char* msisdn;//手機号碼
char* name;//使用者名稱
int netAge;//網齡
} MobileUser;
//比較網齡
namespace std
{
template<>
struct less<MobileUser> :
public binary_function<MobileUser,MobileUser,bool>
{
bool operator()(const MobileUser& rhs1,const MobileUser& rhs2) const
{
return rhs1.netAge < rhs2.netAge;
}
};
}
#ifdef __cplusplus
extern "C" {
#endif
//計算規則處理基類
class BusinessRuleProcessor {
public:
BusinessRuleProcessor();
virtual void calcRule(deque<MobileUser> input) = 0;
static int getResultSize(){return output.size();}
static deque<MobileUser> getResult(){return output;}
protected:
static deque<MobileUser> output;
};
//取網齡最小的topN
class MinTopNNetAgeProcessor : public BusinessRuleProcessor {
public:
MinTopNNetAgeProcessor(int topN);
void calcRule(deque<MobileUser> input);
private:
int topN;
};
//取網齡最大的topN
class MaxTopNNetAgeProcessor : public BusinessRuleProcessor {
public:
MaxTopNNetAgeProcessor(int topN);
void calcRule(deque<MobileUser> input);
private:
int topN;
};
//C Stype API for JNA invoke
void minTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN);
void maxTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN);
void findTopNByNetAge(BusinessRuleProcessor* pRuleProcessor,MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser);
//because use malloc/free c-api otherwise lead to memory leaks
void freeMemory(MobileUser* pMobileUser);
#ifdef __cplusplus
}
#endif
#endif // (!__MOBILEUSERPROCESSOR_H__)
MobileUserProcessor.cpp(C++)代碼内容如下:
/**
* @filename:MobileUserProcessor.cpp
*
* Newland Co. Ltd. All rights reserved.
*
* @Description:手機使用者網齡TOPN排名管理類實作
* @author tangjie
* @version 1.0
*
*/
#include "MobileUserProcessor.h"
//計算規則處理基類
deque<MobileUser> BusinessRuleProcessor::output;
BusinessRuleProcessor::BusinessRuleProcessor()
{
BusinessRuleProcessor::output.clear();
}
//取網齡最小的topN
MinTopNNetAgeProcessor::MinTopNNetAgeProcessor(int topN):BusinessRuleProcessor(){this->topN = topN;}
void MinTopNNetAgeProcessor::calcRule(deque<MobileUser> input)
{
if(input.empty()) return;
sort(input.begin(),input.end(),less<MobileUser>());
int interval = (this->topN >= input.size()) ? input.size() : this->topN;
copy(input.begin(),input.begin()+interval,std::back_inserter(output));
}
//取網齡最大的topN
MaxTopNNetAgeProcessor::MaxTopNNetAgeProcessor(int topN):BusinessRuleProcessor(){this->topN = topN;}
void MaxTopNNetAgeProcessor::calcRule(deque<MobileUser> input)
{
if(input.empty()) return;
sort(input.begin(),input.end(),not2(less<MobileUser>()));
int interval = (this->topN >= input.size()) ? input.size() : this->topN;
copy(input.begin(),input.begin()+interval,std::back_inserter(output));
}
//C Stype API for JNA invoke
void findTopNByNetAge(BusinessRuleProcessor* pRuleProcessor,MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser)
{
deque<MobileUser> list(pMobileUser,pMobileUser+numMobileUser);
pRuleProcessor->calcRule(list);
deque<MobileUser> result = BusinessRuleProcessor::getResult();
int number = result.size();
*ppMobileUser = (MobileUser*)malloc(sizeof(MobileUser) * number);
memset(*ppMobileUser, 0, sizeof(MobileUser) * number);
for(int i = 0;i<number;i++)
{
(*ppMobileUser)[i].homeCity = result[i].homeCity;
(*ppMobileUser)[i].netAge = result[i].netAge;
int msisdnLen = strlen(result[i].msisdn)+1;
(*ppMobileUser)[i].msisdn = (char*)malloc(sizeof(char) * msisdnLen);
memset((*ppMobileUser)[i].msisdn, 0, sizeof(char) * msisdnLen);
strcpy((*ppMobileUser)[i].msisdn, result[i].msisdn);
int nameLen = strlen(result[i].name)+1;
(*ppMobileUser)[i].name = (char*)malloc(sizeof(char) * nameLen);
memset((*ppMobileUser)[i].name, 0, sizeof(char) * nameLen);
strcpy((*ppMobileUser)[i].name, result[i].name);
}
return;
}
void minTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN)
{
auto_ptr<BusinessRuleProcessor> pRuleProcessor(new MinTopNNetAgeProcessor(topN));
findTopNByNetAge(pRuleProcessor.get(),pMobileUser,ppMobileUser,numMobileUser);
}
void maxTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN)
{
auto_ptr<BusinessRuleProcessor> pRuleProcessor(new MaxTopNNetAgeProcessor(topN));
findTopNByNetAge(pRuleProcessor.get(),pMobileUser,ppMobileUser,numMobileUser);
}
void freeMemory(MobileUser* pMobileUser)
{
deque<MobileUser> result = BusinessRuleProcessor::getResult();
for(int i = 0;i<result.size();i++)
{
free(result[i].msisdn);
free(result[i].name);
}
free(pMobileUser);
}
MobileUserProcessor.java(Java)代碼内容如下:
/**
* @filename:MobileUserProcessor.java
*
* Newland Co. Ltd. All rights reserved.
*
* @Description:手機使用者網齡TOPN排名管理JNA類實作
* @author tangjie
* @version 1.0
*
*/
package newlandframework.jna.businessrule;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.ByReference;
import com.sun.jna.ptr.PointerByReference;
import java.util.ArrayList;
public class MobileUserProcessor {
public interface JNALibrary extends Library {
public static class MobileUserStruct extends Structure {
public static class ByReference extends MobileUserStruct implements
Structure.ByReference {
}
public int homeCity;
public String msisdn;
public String name;
public int netAge;
public MobileUserStruct() {
}
public MobileUserStruct(Pointer p) {
super(p);
}
}
//網齡最大topN
public void maxTopNByNetAge(MobileUserStruct.ByReference vals,
PointerByReference valsRef, int numVals, int topN);
//網齡最小topN
public void minTopNByNetAge(MobileUserStruct.ByReference vals,
PointerByReference valsRef, int numVals, int topN);
//釋放JNA記憶體子產品
public void freeMemory(Pointer p);
}
//通過JNA通路調用C++的實作
public static void invoke(ArrayList<JNALibrary.MobileUserStruct> list,
ArrayList<JNALibrary.MobileUserStruct> listResult,int topN) {
JNALibrary library;
JNALibrary.MobileUserStruct.ByReference refMobileUser = null;
//和C++的指針的指針綁定映射,其内容為儲存處理結果的首位址
PointerByReference refPtrVal;
try {
library = (JNALibrary) Native.loadLibrary(
"../src/libMobileUserProcessor.so", JNALibrary.class);
refMobileUser = new JNALibrary.MobileUserStruct.ByReference();
refPtrVal = new PointerByReference();
} catch (UnsatisfiedLinkError e) {
System.out.println("JNA invoke error");
library = (JNALibrary) Native.loadLibrary(
"./libMobileUserProcessor.so", JNALibrary.class);
refMobileUser = new JNALibrary.MobileUserStruct.ByReference();
refPtrVal = new PointerByReference();
}
//為了相容JNA,用C的方式封裝了C++的邏輯子產品。傳入給C函數的資料,在Java裡面映射成數組Array
JNALibrary.MobileUserStruct[] array = (JNALibrary.MobileUserStruct[]) refMobileUser
.toArray(list.size());
for (int i = 0; i < list.size(); i++) {
array[i].homeCity = list.get(i).homeCity;
array[i].msisdn = list.get(i).msisdn;
array[i].name = list.get(i).name;
array[i].netAge = list.get(i).netAge;
}
//取網齡最大的前topN移動手機使用者
library.maxTopNByNetAge(refMobileUser, refPtrVal, array.length, topN);
//取網齡最小的前topN移動手機使用者
//library.minTopNByNetAge(refMobileUser, refPtrVal, array.length, topN);
//其内容為處理結果數組的首位址
Pointer pValues = refPtrVal.getValue();
JNALibrary.MobileUserStruct refValues = new JNALibrary.MobileUserStruct(
pValues);
refValues.read();
JNALibrary.MobileUserStruct[] arrayResult = (JNALibrary.MobileUserStruct[]) refValues
.toArray(topN);
for (JNALibrary.MobileUserStruct element : arrayResult) {
listResult.add(element);
}
//JNA釋放C++的堆記憶體,否則會記憶體洩露
library.freeMemory(pValues);
}
}
然後,編寫一個用戶端ProcessMain.java傳入要處理的資料,具體參考代碼如下:
/**
* @filename:ProcessMain.java
*
* Newland Co. Ltd. All rights reserved.
*
* @Description:手機網齡排名JNA調用主函數
* @author tangjie
* @version 1.0
*
*/
package newlandframework.jna.businessrule;
import java.util.ArrayList;
import newlandframework.jna.businessrule.MobileUserProcessor.JNALibrary;
public class ProcessMain {
public ProcessMain() {
}
public static void main(String[] args) {
ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> list = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>();
ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> listResult = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>();
//構造一些測試使用者
MobileUserProcessor.JNALibrary.MobileUserStruct userA = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userB = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userC = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userD = new MobileUserProcessor.JNALibrary.MobileUserStruct();
MobileUserProcessor.JNALibrary.MobileUserStruct userE = new MobileUserProcessor.JNALibrary.MobileUserStruct();
userA.homeCity = 591;
userA.msisdn = "5911000";
userA.name = "Jim";
userA.netAge = 2;
list.add(userA);
userB.homeCity = 592;
userB.msisdn = "5921000";
userB.name = "Tom";
userB.netAge = 4;
list.add(userB);
userC.homeCity = 593;
userC.msisdn = "5931000";
userC.name = "John";
userC.netAge = 5;
list.add(userC);
userD.homeCity = 594;
userD.msisdn = "5941000";
userD.name = "Dave";
userD.netAge = 5;
list.add(userD);
userE.homeCity = 595;
userE.msisdn = "5951000";
userE.name = "William";
userE.netAge = 3;
list.add(userE);
//取網齡最大的前3的移動手機使用者
int topN = 3;
//通過JNA調用C++的邏輯處理子產品
MobileUserProcessor.invoke(list, listResult, topN);
//列印處理結果
for (int i = 0; i < listResult.size(); i++) {
System.out.println(listResult.get(i).homeCity);
System.out.println(listResult.get(i).msisdn);
System.out.println(listResult.get(i).name);
System.out.println(listResult.get(i).netAge);
System.out.println("----------------------------------------------");
}
}
}
Jim網齡2年、Tom網齡4年、John網齡5年、Dave網齡5年、William網齡3年。分别找到網齡最大的前3名使用者資訊和網齡最小的前3名使用者資訊。編譯運作過程參考:利用JNA.exe(JNA/C++通路子產品生成器)生成代碼實作Java/C++通信通路 章節。
然後最終的運作結果如下:
處理通路網齡最大的前3名使用者資料:
處理通路網齡最小的前3名使用者資料:
正如我們所預計的結果一樣,完全正确!利用JNA我們很快的把C++子產品和Java子產品,有機的結合在一起了。
寫在最後
本文通過JNA技術,實作了Java和C/C++兩種語言子產品的通信調用,但是,目前僅僅實作了特定通路政策生成子產品的部分代碼。後續有時間,我還會繼續歸納總結出,其它JNA通路政策的通用共性代碼,使得上述架構變得更加靈活、通用。算是為Java/C++的跨語言通信調用,提供另外一種可行的解決方案。當然,要想繼續完善上述的通信架構,對于JNA的進一步深入了解是必不可少的,希望有Geek精神的各位園友們,可以在此基礎上加以改良,不斷完善,讓JNA為我所用。更多的JNA的技術細節大家可以參考:https://jna.java.net/ 上面的API文檔。當然,如果你具備較好的C++功底的話,了解JNA裡面的一些實作方式和功能子產品,會更加地得心應手。洋洋灑灑寫了這麼多,如果面前的你,覺得本文寫得還不錯,對您的學習、工作,有借鑒意義的話,可以點選“推薦”一下,舉手之勞。算是對我辛苦寫部落格的一種肯定和鼓勵吧!
最後,謝謝大家耐心讀完整篇文章!希望我的一點兒實踐和總結,對您有幫助!