JNA——模拟c++中类及成员
前言:
最近正在学习JNA相关知识,但在寻找教程的过程中,发现基本上教程都是针对单个方法的,最多最多介绍至结构体及相关方法的使用。
众所周知,Java是一门面向对象的语言,而C++中也添加了对于类的支持,所以如何通过JNA去调用C++中的类及方法呢?那么接下来就进行介绍.....
目录
C++内容:
我们的最终目标为:构建一个C++中的Person类,在Java中也可以对其进行调用。
首先进行C++中类的构建。
1. Person类(C++)
(1) h文件(Person.h)
先对Person类进行声明,此处沿用面向对象思路,将具体属性隐藏,暴露get与set方法。
#pragma once
class Person
{
private:
wchar_t* name;
int age;
public:
wchar_t* getName();
int getAge();
void setName(wchar_t*);
void setAge(int);
void print();
};
(2)cpp文件(Person.cpp)
具体实现。
#include "pch.h"
#include "Person.h"
#include <iostream>
using namespace std;
wchar_t* Person::getName(){
return name;
}
int Person::getAge(){
return age;
}
void Person::setName(wchar_t* name){
this->name = name;
}
void Person::setAge(int age){
this->age = age;
}
void Person::print(){
cout << "age:" << age << ", name:" << name << endl;
}
2. 外部调用接口
(1)h文件(JNAPerson.h)
先将已经声明及实现的Person类包含进来,再开始声明用于Java中JNA调用的外部接口函数。
可能有同学已经注意到了,除了Person类中已经有的get、set方法以外,还有一个Person_ctor方法,这个方法对应的是Person类的构造函数,返回一个Person对象的指针,且其余方法都会通过这个指针来进行调用。
这么说可能会有点难懂,建议直接看接下来的cpp文件就可以理解了。
#pragma once
#include "Person.h"
#define MYLIBAPI extern "C" _declspec(dllexport) //指的是允许将其给外部调用
MYLIBAPI Person* Person_ctor();
MYLIBAPI wchar_t* getName(Person* person);
MYLIBAPI int getAge(Person* person);
MYLIBAPI void setName(Person* person, wchar_t* name);
MYLIBAPI void setAge(Person* person, int age);
MYLIBAPI void print(Person* person);
(2)cpp文件(JNAPerson.cpp)
可以看到,这些函数都是围绕Person对象的指针进行的,这是因为对于Java而言,我们无需关心C++中类的具体实现,也就无需对Java中的类添加具体的成员变量,我们仅需通过这种方式将Java中的类封装成 “好像它看起来就是一个真的类” 就行了。
#include "pch.h"
#include "JNAPerson.h"
Person* Person_ctor(){
return new Person();
}
wchar_t* getName(Person* person){
return person->getName();
}
int getAge(Person* person){
return person->getAge();
}
void setName(Person* person, wchar_t* name){
person->setName(name);
}
void setAge(Person* person, int age){
person->setAge(age);
}
void print(Person* person){
person->print();
}
3. 预编译标头文件(pch.h)
C++中,最后将JNAPerson.h的头文件添加至预编译即可,在通过编译后,得到了dll文件
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
#include "JNAPerson.h"
#endif //PCH_H
Java内容:
在C++获得dll文件之后,将其放置于工程根目录下
1. JNA相关接口(dllTest.java)
该接口继承的是com.sun.jna中的Library接口。
首先进行dll的加载,需要注意的是 loadLibrary 方法中含有两个参数,第一个参数就是动态链接库不含后缀名的名称,在Windows下就是dll文件的名称。此处通过C++获取的dll文件名为 "DllTest" ,也可自行进行修改,第二个参数则是当前接口.class。
其次进行接口函数的定义,JNA会自动帮你将这些函数去与dll中的函数一一对应起来,但需要注意的是通过JNA转换前后的类型并不一致,可自行进行搜索。
另外,此处接口定义的函数与C++中向JNA暴露的函数可以存在不一致的情况,多一些或者少一些都不影响。
package com.study.test.jna;
import com.sun.jna.*;
public interface DllTest extends Library {
DllTest INSTANCE = (DllTest) Native.loadLibrary(Platform.isWindows() ?
"DllTest" : "", DllTest.class);
Pointer Person_ctor();
WString getName(Pointer pointer);
int getAge(Pointer pointer);
void setName(Pointer pointer, WString name);
void setAge(Pointer pointer, int age);
void print(Pointer pointer);
}
2. Person类(Java)
其实对于Java中类的对象,最根本的还是需要与C++中的对象一 一对应,所以此处在构造函数中,将C++的对象指针保存至self中,后续全部的功能调用也是通过该指针,而暴露在外的功能也可正常编写进行调用了~
另外需要注意的是,此处全部方法的实现都是需要通过JNA接口中的INSTANCE实例进行调用的。
package com.study.test.jna;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
public class Person {
private final Pointer self; // 对应C++中的Person对象指针
Person(){
self = DllTest.INSTANCE.Person_ctor();
}
String getName(){
return DllTest.INSTANCE.getName(self).toString();
}
int getAge(){
return DllTest.INSTANCE.getAge(self);
}
void setName(String name){
DllTest.INSTANCE.setName(self, new WString(name));
}
void setAge(int age){
DllTest.INSTANCE.setAge(self, age);
}
void print(){
DllTest.INSTANCE.print(self);
}
}
3. 测试
package com.study.test.jna;
public class TestDemo {
public static void main(String[] args) {
Person person = new Person();
person.setAge(22);
person.setName("B");
System.out.println("This is age:"+person.getAge());
System.out.println("This is name:"+person.getName());
person.print();
}
}