0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

CHANBAEK 来源:嵌入式大本营 作者:小小飞飞哥 2023-05-23 15:41 次阅读

大家好,今天分享的是使用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文件,这个头文件是不可修改的,大概长这样

图片

接下来新建一个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"

图片

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

图片

四、编译并运行java程序

在命令行中输入

javac Java2cpp.java

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

最后在命令行中输入

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

就可以运行java程序了。

图片

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

图片

图片

总结:

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

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

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

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

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

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

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

    关注

    3

    文章

    3545

    浏览量

    88706
  • JAVA
    +关注

    关注

    19

    文章

    2967

    浏览量

    104757
  • 串口
    +关注

    关注

    14

    文章

    1554

    浏览量

    76522
  • C++
    C++
    +关注

    关注

    22

    文章

    2108

    浏览量

    73651
  • 动态库
    +关注

    关注

    0

    文章

    17

    浏览量

    6233
收藏 人收藏

    评论

    相关推荐

    Qt创建动态C#调用,通过回调完成交互

    动态(dll),给C#加载调用,并且还需要设置回调,方便C#知道Qt运行时,输出内部的一些实时
    的头像 发表于 09-09 11:37 4875次阅读
    Qt创建<b class='flag-5'>动态</b><b class='flag-5'>库</b>给<b class='flag-5'>C</b>#<b class='flag-5'>调用</b>,通过回调完成交互

    C/ C++/ Java 程序设计经典教程》

    C语言的标准 1.9 重要的软件发展趋势:面向对象的威廉希尔官方网站 1.10 C++语言以及C++编程 1.11 Java语言以及
    发表于 11-13 11:22

    JAVAC++区别

    指针来直接访问内存无指针,并且增添了自动的内存管理功能,从而有效地防止了cc++语言中指针操作失误,如野指针所造成的系统崩溃。但也不是说JAVA没有指针,虚拟机内部还是使用了指针,只
    发表于 04-11 15:19

    LabVIEW调用visual studio C#生成的动态链接dll文件

    1)visual studio C#生成动态链接dll文件2)LabVIEW通过构造器调用C#
    发表于 07-23 23:18

    JAVAC++区别

    来直接访问内存无指针,并且增添了自动的内存管理功能,从而有效地防止了cc++语言中指针操作失误,如野指针所造成的系统崩溃。但也不是说JAVA没有指针,虚拟机内部还是使用了指针,只是外
    发表于 10-10 14:50

    请问各位labview大佬关于调用dll动态链接的问题

    我最近学习labview的dll动态调用,我用的vs2017来编译c++并生成dll文件,但是
    发表于 03-10 18:43

    JavaC++的区别

    来直接访问内存无指针,并且增添了自动的内存管理功能,从而有效地防止了cc++语言中指针操作失误,如野指针所造成的系统崩溃。但也不是说JAVA没有指针,虚拟机内部还是使用了指针,只是外
    发表于 09-13 16:02

    labview 动态调用 C# 生成的dll

    目的: 用labview动态调用C#生成的dll问题:目前知道可以用.net的构造器调用,但是该方式不像“
    发表于 01-04 15:47

    Tcl/Tk命令与C/C++的集成研究

    针对 Tcl/Tk 脚本中需要调用C/C++函数的问题,简要说明了Tcl/Tk 命令的运行机理,给出了一个使用Tcl/Tk 命令来调用C/
    发表于 08-26 09:47 36次下载

    C++动态链接的创建和调用

    动态连接的创建步骤: 一、创建Non-MFC DLL动态链接 1、打开File —> New —> Project选项,选择Win3
    发表于 11-24 18:13 7次下载

    什么是DLL (动态连接)

    什么是DLL (动态连接) DLL动态连接),既然是
    发表于 11-28 14:51 1.5w次阅读

    C语言与C++相互调用

        1CC++相互调用 在一个嵌入式系统中大部分的底层和驱动层更多的是采用C语言来进行开发,而上层应用、服务更多的采用C++等高级语言
    的头像 发表于 01-18 11:05 3332次阅读
    <b class='flag-5'>C</b>语言与<b class='flag-5'>C++</b>相互<b class='flag-5'>调用</b>

    LABVIEW调用C# DLL实例分享

    LABVIEW调用C# DLL实例分享
    发表于 01-04 11:20 106次下载

    java上位机开发(c调用)

    所有的动态语言,包括在java在内,一般都会提供一个调用c的方法。java语言的出现是在上个世
    发表于 05-09 10:00 0次下载
    <b class='flag-5'>java</b>上位机开发(<b class='flag-5'>c</b><b class='flag-5'>库</b><b class='flag-5'>调用</b>)

    CC++编写环境下LabVIEW如何调用动态

    C语言编写的动态链接相比,不同的地方在于extern int “C” __declspec(dllexport) add(int x,int y) 这一导出语句,在
    发表于 06-11 09:15 7778次阅读
    <b class='flag-5'>C</b>和<b class='flag-5'>C++</b>编写环境下LabVIEW如何<b class='flag-5'>调用</b><b class='flag-5'>动态</b><b class='flag-5'>库</b>?