Java调用C/C++动态库dll操作串口实例

描述

大家好,今天分享的是使用C/C++编写一个读取串口数据的代码,然后将其编译成Windows下的动态链接库(.dll文件),然后写一个简单的java demo来调用C/C++接口。

众所周知,java开发项目会比较方便,尤其是在一些大型项目中,java开发效率会比较高,但是一些底层的东西和一些追求效率的东西,依然会倾向使用C/C++,这是他们不可替代的优势。所以有时会需要两者混合起来,C/C++完成一部分较底层的功能,提供接口给java调用。

由于本人主要是从事嵌入式相关,对java也不是很熟,所以今天主要是把整个流程过一遍,把整个流程打通,搞清楚如何制作动态库,如何被java调用即可。关于动态库的内容,还可以参考我之前的文章。

还有就是今天所有的操作都是在命令行中完成,不会使用任何IDE ,这也符合我们嵌入式开发的习惯,能用命令行就没必要去安装臃肿的IDE软件,所以大家需要先在自己的DOS窗口中安装好必要的工具,g++用于编译c++代码,安装java的jdk以提供java环境,另外把nmake路径添加到环境变量中就可以使用Makefile了。

一、编写cpp功能函数

这一部分就是具体的功能实现,比如在本次中,我们需要读取串口数据,那么至少涉及四个接口:打开串口,设置波特率等参数,读取数据,关闭串口。我们需要使用c++代码把这四个接口的具体实现写出来,新建一个文件夹,用于存放文件,在里面新建一个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);    //设置串口信息
    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"<

这里面主要是涉及两个很重要的函数,CreateFile 和 ReadFile 函数,这两个函数是Windows下的API,可以直接调用,关于具体的函数功能及用法,这里暂时不讨论,其实和linux下的驱动是很类似的。

二、编写一个java的demo

Java2cpp.java :

public class Java2cpp
{
static
{
System.loadLibrary("javaCallcpp");
}


public native boolean DLL_OPEN();
public native void DLL_Set(int baud);    //设置串口信息
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" );导入了一个库,这个库的名字是javaCallcpp 也就是说待会儿我们要生成一个javaCallcpp.dll的文件。第二点是public native boolean DLL_OPEN ();等几个API。

使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。

这个时候还没有dll文件,因此有了第三步:

三、生成dll文件

首先进入到文件目录,在命令行中使用

javac -h ./ Java2cpp.java

命令生成Java2cpp.h文件,这个头文件是不可修改的,大概长这样

C++

接下来新建一个Java2cpp.cpp文件,在这个文件中调用第一步中实现的接口

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[] 转化为jstring
    //定义java String类 strClass
     jclass strClass = (env)->FindClass("Ljava/lang/String;");
     //获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
    jmethodID ctorID = (env)->GetMethodID(strClass, ", "([BLjava/lang/String;)V");
    //建立byte数组
    jbyteArray bytes = (env)->NewByteArray((jsize)strlen(buf));
    //将char* 转换为byte数组
    (env)->SetByteArrayRegion(bytes, 0, (jsize)strlen(buf), (jbyte*)buf);
    //设置String, 保存语言类型,用于byte数组转换至String时的参数
    jstring encoding = (env)->NewStringUTF("gbk"); 
    //将byte数组转换为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;
}

实际上你也可以直接在这里实现具体的功能代码,这样就省掉第一步了,不过为了一个分层的思想,更方便维护,还是不要省掉第一步比较好。

在这个文件中,使用#include "dllApi.h" 来调用第一步中的接口,然后这个文件是被java程序调用的,这里要稍微注意一下数据类型的转化。 比如char[] 转化为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"

C++

这样在目录中就出现了javaCallcpp.dll文件。

C++

四、编译并运行java程序

在命令行中输入

javac Java2cpp.java

生成Java2cpp.class文件,.class文件就是java编译后的可执行文件

最后在命令行中输入

java Java2cpp  //注意没有.class后缀

就可以运行java程序了。

C++

这样就成功实现了java调用dll库,我们也可以将上面那些命令写成Makefile文件,和linux下的Makefile是一样的,只不过在Windows下不是make命令,而是nmake,使用时需要将nmake的路径添加到系统环境变量中。

C++

C++

总结:

1、编写cpp具体的功能接口代码

2、编写java程序,使用native关键字声明调用本地接口

3、javac -h (在旧版本中直接使用javah)生成头文件,根据头文件编写对应cpp源文件

4、使用g++ 编译生成.dll文件

5、使用javac xxx.java生成xxx.class文件并执行

当然如果不习惯使用命令行,也可以结合Visual Studio 和 Eclipse 两个IDE进行操作,在这里不做阐述。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分