最近做项目使用JNI引用C++的dll,在此记录遇到的问题。
1.如何导出C++中的类
大家都知道JNI只能导出全局函数,至少我搜索了好久都没看到过有导出类的,貌似不可以。当然如果可以就更好了。
那么既然使用的是C++语言,如果不使用类的话,那还不如直接使用C。总之,面向对象编程就是比面向过程”艺术”。所以就遇到个问题,C++类中的成员函数如何使用JNI导出?JNI只能导出全局函数。
所以,我们可以采用一个折中的办法,为每一个类成员函数写一个全局导出函数,在cpp实现文件中new 一个类对象,在全局函数中使用这个类对象调用成员函数,最后写一个全局函数释放这个类对象。
例如:
//Dog.h
class Dog()
{
public:
Dog();
~Dog();
void eat(){std::cout<<"eat bone\n";};
...
}
要想通过JNI导出Dog类的eat成员函数,当然可以有多个成员函数,我们新建一个文件:
//export.h
#include "jni.h"
extern "C"
{
JNIEXPORT void JNICALL JAVA_com_wj_demo_init(JNIEnv *env, jobject);
JNIEXPORT void JNICALL JAVA_com_wj_demo_eat(JNIEnv *env, jobject);
JNIEXPORT void JNICALL JAVA_com_wj_demo_release(JNIEnv *env, jobject);
}
因为成员函数必须使用类的实例调用,所以我们可以在export.cpp中实例化这个类:
//export.cpp
#include "Dog.h"
Dog *dog ;
JNIEXPORT void JNICALL JAVA_com_wj_demo_init(JNIEnv *env, jobject)
{
dog = new Dog();
}
JNIEXPORT void JNICALL JAVA_com_wj_demo_eat(JNIEnv *env, jobject)
{
dog->eat();
}
JNIEXPORT void JNICALL JAVA_com_wj_demo_release(JNIEnv *env, jobject)
{
dog->~Dog();
}
这样大概就可以了。手写的代码,不保证能运行,但是就是这思路。
2.在java中System.loadLibrary()注意的问题
<1>如果是64为的jdk必须加载64位的dll,debug版本的和release版本的都没关系,可以相互调用,但是必须是64位的。
<2>必须先加载依赖库,比如你要引用的a.dll还引用着b.dll,就必须先使用System.loadLibrary加载b.dll,再加载a.dll,有时候一个dll依赖多个dll的时候,需要注意加载的顺序。
<3>有些64位的dll引用的竟然是32位的dll,竟然也能编译过了。比如,我的项目中引用libcurl.dll,再loadLibrary的时候,报 %1 libcurl不是win32应用程序,我就纳闷了。然后在dependency工具下看到其引用的SSLEAY32.dll和LIBEAY32.dll竟然是32位的,估计是系统目录下的,因为我引用的dll目录中没有这两个dll,如果没有找的话,编译Java程序的时候会报找不到依赖库,但是没报这个错,说明是在系统目录下找到的。所以,我找到了SSLEAY32.dll和LIBEAY32.dll这两个的64位版本的,就可以了。