最近在做一个项目,需要用到C++编写的程序,但是需要java调用·,现在项目结束,总结一下经验。如果不需要java与C++项目之间参数传递,可直接看第8步。
1.首先我们需要两个.h的文件,一个在路径在jdk的include下jni.h
第二个使在jdk下的include下的jni_md.h
2.在java中建立一个类,类名随便起
里面的DLL文件名是我们即将要生成的DLL的文件名,通过静态方法加载我们即将生成的文件名,这个DLL文件名要记住,后面要注意,这个地方为什么要用这个方式加载,而不用网上常见的通过JNA的jar里面的方式加载呢,这是由于对于C++项目生成的DLL在java中可以增加他的健壮性,对于参数的传递在java具有更好的适应性。说白了就是减少他的bug出现的概率。
public class DLLtest {
public native String sayhello(String hello);
static {
// System.load()方法是通过加载指定路径下DLL的加载方式。
System.load("D:\\DLLSayHello.dll");
//System.loadLibrary()方法是通过加载DLL的文件名即可,但是需要把他放在自己电脑System32下才能读取到,要是想要上线不建议这种方法
// System.loadLibrary("DLLSayHello.dll");
}
}
native 是一个关键字,sayhello方法是我们DLL中main()函数写的东西,或者说是我们调用DLL里面的DLL的实现方法。
3.写完了这个类之后需要生成相应的一个.h文件,在C++的项目中需要用到。在这里就需要用到一个命令。首先要打开我们编写的这个类所在的位置,在cmd窗口里打开到那个目录。
输入下面命令,jdk1.6以前的版本如果不行可以自己百度,其中. 后面有空格,需注意
javac -encoding utf8 -h . DLLtest.java
会生成一个.h和.class的文件,要.h不要.class,.class可以不用管。
这个地方有个坑需要注意,也是我调了一段时间才发现的,这个地方首先要明白idea创建普通文件目录和标记为源根文件的区别,我们把这java的测试类放到了com.zlst.dll的包下面,所以在生成的前缀中会有com_zlst_dll这样的标志,在我们生成的DLL后,我们的测试类位置就不能再移动了,否则生成的DLL文件我们就调用不了,他会识别这个包名,这是个坑,需要切记!!!
以上java端的准备工作就做完了。
4. 编写C++的DLL,这个地方介绍两种,一种是我们有自己的C++项目,不需要写方法,另一种是我们自己手动写DLL项目,我感觉大家都会是第一种,第二种自己编写DLL的要么是java实现不了,要么是语言不互通。用C++写简单。
首先我们介绍第一种,就是将普通的C++项目直接改写为DLL,我用的是vs2015.
打开我们的项目,将调试环境改写为Release + 64,因为我们的jdk就是64位的,所以调试环境必须这样写。如果此时你编译自己的项目有问题,那就需要改写你自己的代码了,这一个很难受的过程,如果没有问题,就打开工具栏的 调试—>属性->配置属性->常规->配置类型 选择动态链接库。
5 将前期准备的三个.h头文件加载到项目种去,我们可以看到通过命令行生成的com_zlst_dll_DLLtest.h 里的内容。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zlst_dll_DLLtest */
#ifndef _Included_com_zlst_dll_DLLtest
#define _Included_com_zlst_dll_DLLtest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_zlst_dll_DLLtest
* Method: sayhell
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zlst_dll_DLLtest_sayhell
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
第二行的#include <jni.h>需要改写位#include "jni.h"因为这是外部头文件,不是系统的。然后找到我们将里面的
JNIEXPORT jstring JNICALL Java_com_zlst_dll_DLLtest_sayhell
(JNIEnv *, jobject, jstring)
替换到我们的main函数的开头就ok了,然后直接生成DLL文件就可以用了。jstring 是我们传递值得,这个地方需要自己起个名,方便函数体调用,有关jstring类型得值转化为C++可以识别的值在下面会贴出来,如果有时间得话。
6 如果是自己写一个新的和有以前的项目一样,依然要把三个.h文件加载到C++项目里,项目就按照C++正常得写法一样,但是main函数 int main()要换成
JNIEXPORT jstring JNICALL Java_com_zlst_dll_DLLtest_sayhell
(JNIEnv *, jobject, jstring)
但是你要在写这个函数要引用头文件 com_zlst_dll_DLLtest.h 才可以,注意事项看步骤5咯。
7 下面就是测试生成得DLL
在你调用这个DLL的方法里这样写:
DLLtest dlLtest=new DLLtest();
System.out.println(dlLtest.sayhell("hello"));
就OK了。
8 如果你不需要参数传递,那你可以不用这么麻烦,直接写一个DLL项目,生成DLL之后,在java种这样调用。
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface CLibrary extends Library {
CLibrary INSTANCE =
Native.load("E:\\GetSystemInfo.dll",CLibrary.class);
//start()是C++实现的接口
int start();
}
不过这样写需要自己下载一个jar包JNA,这个小伙伴自己百度下载即可。
9 附上java与C++传递参数时需要的类型转化函数
WCHAR* toWchar(string strZipPath) {
string strZipPath_un = strZipPath;
//将路径转为TCHAR类型
int iUnicode = MultiByteToWideChar(CP_ACP, 0, strZipPath_un.c_str(), strZipPath_un.length(), NULL, 0);
WCHAR* pwUnicode = new WCHAR[iUnicode + 2];
if (pwUnicode)
{
ZeroMemory(pwUnicode, iUnicode + 2);
}
MultiByteToWideChar(CP_ACP, 0, strZipPath_un.c_str(), strZipPath_un.length(), pwUnicode, iUnicode);
pwUnicode[iUnicode] = '\0';
pwUnicode[iUnicode + 1] = '\0';
return pwUnicode;
}
jstring charTojstring(JNIEnv* env, const char* pat) {
//定义java String类 strClass
jclass strClass = (env)->FindClass("Ljava/lang/String;");
//获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
// 设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF("utf8");
//将byte数组转换为java String,并输出
return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}
/**
* unsigned char 转成 jstring 类型
* @param e
* @param pJobject
* @param pChar
* @return
*/
jstring unsigchar2jstring(JNIEnv *e, unsigned char* pChar) {
unsigned char *newresult = pChar;
//定义java String类 clsstring
jclass clsstring = e->FindClass("java/lang/String");
//获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID mid = e->GetMethodID(clsstring, "<init>", "([BLjava/lang/String;)V");
// 设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = e->NewStringUTF("GB2312");
//建立byte数组
jbyteArray bytes = e->NewByteArray(strlen((char*)newresult));
//将char* 转换为byte数组
e->SetByteArrayRegion(bytes, 0, strlen((char*)newresult), (jbyte*)newresult);
//将byte数组转换为java String,并输出
return (jstring)e->NewObject(clsstring, mid, bytes, encoding);
}
char* jstringToChar(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0) {
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}
jstring stringTojstring(JNIEnv* env, const char* pat) {
jclass strClass = (env)->FindClass("java/lang/String");
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *)pat);
jstring encoding = (env)->NewStringUTF("GB2312");
return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}
string unsignedCharToHexString(unsigned char ch) {
const char hex_chars[] = "0123456789abcdef";
unsigned int highHalfByte = (ch >> 4) & 0x0f;
unsigned int lowHalfByte = (ch & 0x0f);
string result;
result += hex_chars[highHalfByte];
result += hex_chars[lowHalfByte];
return result;
}
void TcharToChar(const TCHAR * tchar, char * _char)
{
int iLength;
//获取字节长度
iLength = WideCharToMultiByte(CP_ACP, 0, tchar, -1, NULL, 0, NULL, NULL);
//将tchar值赋给_char
WideCharToMultiByte(CP_ACP, 0, tchar, -1, _char, iLength, NULL, NULL);
}
//
如果需要源码可以私聊我哦!