C++项目通过编译成DLL使java调用

本文详细介绍了如何在Java项目中调用C++编写的DLL,包括使用JNI、头文件处理、类定义、参数转换和不同加载方法,旨在提升项目的健壮性和适应性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做一个项目,需要用到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);
}
//

如果需要源码可以私聊我哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值