大家好,今天分享的是使用C/C++編寫一個讀取串口數(shù)據(jù)的代碼,然后將其編譯成Windows下的動態(tài)鏈接庫(.dll文件),然后寫一個簡單的java demo來調(diào)用C/C++接口。
眾所周知,java開發(fā)項目會比較方便,尤其是在一些大型項目中,java開發(fā)效率會比較高,但是一些底層的東西和一些追求效率的東西,依然會傾向使用C/C++,這是他們不可替代的優(yōu)勢。所以有時會需要兩者混合起來,C/C++完成一部分較底層的功能,提供接口給java調(diào)用。
由于本人主要是從事嵌入式相關(guān),對java也不是很熟,所以今天主要是把整個流程過一遍,把整個流程打通,搞清楚如何制作動態(tài)庫,如何被java調(diào)用即可。關(guān)于動態(tài)庫的內(nèi)容,還可以參考我之前的文章。
還有就是今天所有的操作都是在命令行中完成,不會使用任何IDE ,這也符合我們嵌入式開發(fā)的習(xí)慣,能用命令行就沒必要去安裝臃腫的IDE軟件,所以大家需要先在自己的DOS窗口中安裝好必要的工具,g++用于編譯c++代碼,安裝java的jdk以提供java環(huán)境,另外把nmake路徑添加到環(huán)境變量中就可以使用Makefile了。
一、編寫cpp功能函數(shù)
這一部分就是具體的功能實現(xiàn),比如在本次中,我們需要讀取串口數(shù)據(jù),那么至少涉及四個接口:打開串口,設(shè)置波特率等參數(shù),讀取數(shù)據(jù),關(guān)閉串口。我們需要使用c++代碼把這四個接口的具體實現(xiàn)寫出來,新建一個文件夾,用于存放文件,在里面新建一個dllApi.cpp和dllApi.h文件。
dllApi.h:
#ifndef DLLAPI_H
#define DLLAPI_H
#include
#include
#include
#include
using namespace std;
class ComHelper {
public:
// bool Open(void); //打開串口
void DLL_API_Set(int baud); //設(shè)置串口信息
char* DLL_API_Read(char str[],int length);
bool DLL_API_Close(void);
bool DLL_API_OPEN(void);
};
dllApi.cpp:
#include "dllApi.h"
HANDLE hCom ;
bool ComHelper::DLL_API_OPEN(void)
{
int num;
const char* com="COM";
char buf[100]={0};
cout<<"請輸入要打開的串口號,輸入1打開COM1"<
這里面主要是涉及兩個很重要的函數(shù),CreateFile 和 ReadFile 函數(shù),這兩個函數(shù)是Windows下的API,可以直接調(diào)用,關(guān)于具體的函數(shù)功能及用法,這里暫時不討論,其實和linux下的驅(qū)動是很類似的。
二、編寫一個java的demo
Java2cpp.java :
public class Java2cpp
{
static
{
System.loadLibrary("javaCallcpp");
}
public native boolean DLL_OPEN();
public native void DLL_Set(int baud); //設(shè)置串口信息
public native String DLL_Read(char str[],int length);
public native boolean DLL_Close();
public static void main(String args[])
{
System.out.println("code test....");
boolean ret;
char buf[]={0};
String str;
Java2cpp com = new Java2cpp();
ret=com.DLL_OPEN();
if(!ret)
{
System.out.println("打開串口失敗");
return;
}
System.out.println("打開串口成功");
com.DLL_Set(115200);
while(true)
{
str=com.DLL_Read(buf, 100);
System.out.println(str);
}
}
}
這里主要注意兩個點,一個是使用System.loadLibrary( "javaCallcpp" );導(dǎo)入了一個庫,這個庫的名字是javaCallcpp 也就是說待會兒我們要生成一個javaCallcpp.dll的文件。第二點是public native boolean DLL_OPEN ();等幾個API。
使用native關(guān)鍵字說明這個方法是原生函數(shù),也就是這個方法是用C/C++語言實現(xiàn)的,并且被編譯成了DLL,由java去調(diào)用。使用native關(guān)鍵字說明這個方法是原生函數(shù),也就是這個方法是用C/C++語言實現(xiàn)的,并且被編譯成了DLL,由java去調(diào)用。這些函數(shù)的實現(xiàn)體在DLL中,JDK的源代碼中并不包含,你應(yīng)該是看不到的。對于不同的平臺它們也是不同的。這也是java的底層機制,實際上java就是在不同的平臺上調(diào)用不同的native方法實現(xiàn)對操作系統(tǒng)的訪問的。
這個時候還沒有dll文件,因此有了第三步:
三、生成dll文件
首先進入到文件目錄,在命令行中使用
javac -h ./ Java2cpp.java
命令生成Java2cpp.h文件,這個頭文件是不可修改的,大概長這樣
接下來新建一個Java2cpp.cpp文件,在這個文件中調(diào)用第一步中實現(xiàn)的接口
Java2cpp.cpp :
#include "Java2cpp.h"
#include "dllApi.h"
JNIEXPORT jboolean JNICALL Java_Java2cpp_DLL_1OPEN
(JNIEnv *, jobject)
{
ComHelper com;
bool var=0;
var=com.DLL_API_OPEN();
return var;
}
JNIEXPORT void JNICALL Java_Java2cpp_DLL_1Set
(JNIEnv *, jobject, jint baud)
{
ComHelper com;
com.DLL_API_Set(115200);
return;
}
JNIEXPORT jstring JNICALL Java_Java2cpp_DLL_1Read
(JNIEnv * env, jobject, jcharArray ay, jint)
{
char array[1024];
char* buf=array;
int len=strlen(buf);
jstring ret;
ComHelper com;
com.DLL_API_Read(array,100);
//將char[] 轉(zhuǎn)化為jstring
//定義java String類 strClass
jclass strClass = (env)->FindClass("Ljava/lang/String;");
//獲取java String類方法String(byte[],String)的構(gòu)造器,用于將本地byte[]數(shù)組轉(zhuǎn)換為一個新String
jmethodID ctorID = (env)->GetMethodID(strClass, ", "([BLjava/lang/String;)V");
//建立byte數(shù)組
jbyteArray bytes = (env)->NewByteArray((jsize)strlen(buf));
//將char* 轉(zhuǎn)換為byte數(shù)組
(env)->SetByteArrayRegion(bytes, 0, (jsize)strlen(buf), (jbyte*)buf);
//設(shè)置String, 保存語言類型,用于byte數(shù)組轉(zhuǎn)換至String時的參數(shù)
jstring encoding = (env)->NewStringUTF("gbk");
//將byte數(shù)組轉(zhuǎn)換為java String,并輸出
ret= (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
return ret;
}
JNIEXPORT jboolean JNICALL Java_Java2cpp_DLL_1Close
(JNIEnv *, jobject)
{
bool ret;
ComHelper com;
ret=com.DLL_API_Close();
return ret;
}
實際上你也可以直接在這里實現(xiàn)具體的功能代碼,這樣就省掉第一步了,不過為了一個分層的思想,更方便維護,還是不要省掉第一步比較好。
在這個文件中,使用#include "dllApi.h" 來調(diào)用第一步中的接口,然后這個文件是被java程序調(diào)用的,這里要稍微注意一下數(shù)據(jù)類型的轉(zhuǎn)化。 比如char[] 轉(zhuǎn)化為jstring。
然后在命令行中將前面的dllApi.cpp 和這個Java2cpp.cpp同時編譯成dll文件。
g++ -shared -fPIC Java2cpp.cpp dllApi.cpp -o javaCallcpp.dll -I "F:\\Program Files\\Java\\jdk-11.0.12\\include" -I "F:\\Program Files\\Java\\jdk-11.0.12\\include\\win32"
這樣在目錄中就出現(xiàn)了javaCallcpp.dll文件。
四、編譯并運行java程序
在命令行中輸入
javac Java2cpp.java
生成Java2cpp.class文件,.class文件就是java編譯后的可執(zhí)行文件
最后在命令行中輸入
java Java2cpp //注意沒有.class后綴
就可以運行java程序了。
這樣就成功實現(xiàn)了java調(diào)用dll庫,我們也可以將上面那些命令寫成Makefile文件,和linux下的Makefile是一樣的,只不過在Windows下不是make命令,而是nmake,使用時需要將nmake的路徑添加到系統(tǒng)環(huán)境變量中。
總結(jié):
1、編寫cpp具體的功能接口代碼
2、編寫java程序,使用native關(guān)鍵字聲明調(diào)用本地接口
3、javac -h (在舊版本中直接使用javah)生成頭文件,根據(jù)頭文件編寫對應(yīng)cpp源文件
4、使用g++ 編譯生成.dll文件
5、使用javac xxx.java生成xxx.class文件并執(zhí)行
當(dāng)然如果不習(xí)慣使用命令行,也可以結(jié)合Visual Studio 和 Eclipse 兩個IDE進行操作,在這里不做闡述。
-
WINDOWS
+關(guān)注
關(guān)注
4文章
3608瀏覽量
91021 -
JAVA
+關(guān)注
關(guān)注
20文章
2986瀏覽量
107059 -
串口
+關(guān)注
關(guān)注
14文章
1586瀏覽量
79040 -
C++
+關(guān)注
關(guān)注
22文章
2117瀏覽量
74830 -
動態(tài)庫
+關(guān)注
關(guān)注
0文章
17瀏覽量
6329
發(fā)布評論請先 登錄
Qt創(chuàng)建動態(tài)庫給C#調(diào)用,通過回調(diào)完成交互

《C/ C++/ Java 程序設(shè)計經(jīng)典教程》
JAVA和C++區(qū)別
LabVIEW調(diào)用visual studio C#生成的動態(tài)鏈接庫dll文件
JAVA和C++區(qū)別
請問各位labview大佬關(guān)于調(diào)用dll動態(tài)鏈接庫的問題
Java和C++的區(qū)別
labview 動態(tài)調(diào)用 C# 生成的dll
Tcl/Tk命令與C/C++的集成研究
C++中動態(tài)鏈接庫的創(chuàng)建和調(diào)用
C語言與C++相互調(diào)用

java上位機開發(fā)(c庫調(diào)用)

C和C++編寫環(huán)境下LabVIEW如何調(diào)用動態(tài)庫?

評論