JNI调用dll库或so库

一、应用场景

        如果想用Java调用C或C++程序,前提是给定了C或C++的动态库dll(Windows)或so(Linux)文件和函数头文件说明,根据头文件编写JNI文件,最后根据JNI文件编写Java程序。

二、应用过程

1、Windows环境

1.1、首先得到dll库文件和头文件说明

 ​​

1.2、根据头文件编写JNI文件

1.2.1、编写JNI头文件cn.bk.test.Native.h

//引入jni头
#include <jni.h>

//此处声明具体的函数
extern "C" {

    /*
     * Class:     cn_bk_test_Native
     * Method:    comEncryptionProcEX
     * Signature: (I[BI[B[I)I
     */
    JNIEXPORT jint JNICALL Java_cn_bk_test_Native_comEncryptionProcEX
    (JNIEnv*, jclass, jint, jint, jint, jbyteArray, jint, jbyteArray, jintArray);

}

  1.2.2、编写JNI类文件cn.bk.test.Native.c

 

//引入头
#include "cn_bk_test_Native.h"


//----------------------------------------------------------------------------
// 功能	调用密码机上的对称密钥索引号,进行加解密
// 输入	comID		输入		输入:算法标识						输出:无
//		keyIndex	输入		输入:指定对称密钥索引号		        输出:无
//		openSeal	输入		输入:加/解密标志 					输出:无
//		inData		输入	 	输入:明文/密文 					输出:无
//		inLen		输入		输入:明文/密文长度 				输出:无
//		outData		输入/输出	输入:密文/明文存储空间 				输出:密文/明文
//		outLen		输入/输出	输入:密文/明文存储空间长度 		    输出:密文/明文长度
// 返回	0:  0
//		错误,返回错误代码
//----------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_cn_bk_test_Native_comEncryptionProcEX
(JNIEnv* env, jclass jobj, jint comID, jint keyIndex, jint openSeal, jbyteArray inData, jint inLen, jbyteArray outData, jintArray outLen) {

	unsigned char* c_inData = (*env)->GetByteArrayElements(env, inData, NULL);
	unsigned char* c_outData = (*env)->GetByteArrayElements(env, outData, NULL);
	int* c_outLen = (*env)->GetIntArrayElements(env, outLen, NULL);

	int ret = comSymmetryCryptionProc_EX(comID, keyIndex, openSeal, c_inData, inLen, c_outData, c_outLen);

	(*env)->ReleaseByteArrayElements(env, inData, c_inData, 0);
	(*env)->ReleaseByteArrayElements(env, outData, c_outData, 0);
	(*env)->ReleaseIntArrayElements(env, outLen, c_outLen, 0);

	return ret;
}

注意:

  • JNI中的函数的命名规则:Java_(Java中的包名)cn_bk_test_(Java中的类名)Native_(Java中的方法名)comEncryptionProcEX
  • JNI函数中不存在方法重构,即方法名不能重复

1.3、根据编写的JNI文件生成Java直接调用的dll文件

1.3.1、下载安装mingw_64位, 并将其安装目录配置到系统环境变量 

        mingw_64位最新下载地址:https://sourceforge.net/projects/mingw-w64/files/


        该软件的作用是在windows系统使用gcc和g++命令,对c文件和c++文件进行编译,生成.o文件, 进而生成.dll文件。64位的只能生成64位的dll文件。若要生成32位的dll文件,请下载32位的mingw.
        mingw_32最新版下载地址:https://sourceforge.net/projects/mingw/files/Installer/
        安装完后将{minw安装目录}\mingw64\bin放到path即可。

1.3.2、生成 .o文件

执行命令: gcc -c -I"%JAVA_HOME%\include" - I"%JAVA_HOME%\include\win32" cn_bk_test_Native.c

1.3.2、生成 .dll文件

执行命令: gcc -Wl,--add-stdcall-alias -shared -o [要生成的dll文件]cn_bk_test_Native.dll [上一步生成的.o文件]cn_bk_test_Native.o [依赖的C程序dll文件]test64.dll

注:如果报系统位数错误则检查C程序性的dll文件编译环境与你此刻编译的系统环境是否相同。

1.4、编写最终调用的Java程序

1.4.1、创建Java工程

1.4.2、创建类:cn.bk.test.Native

public class Native {

    /**
     * 调用密码机上的对称密钥索引号,进行加解密.
     *
     * @param comID    输入   算法标识 0x00200000(sm1) 0x00400000(sm4)
     * @param keyIndex 输入   指定对称密钥索引号(需要使用密码机管理工具,在索引号的位置生成128bit对称密钥)
     * @param openSeal 输入   加/解密标志:0 加密 1 解密
     * @param inData   输入   明文/密文
     * @param inLen    输入   明文/密文长度
     * @param outData  输出   密文/明文存储空间
     * @param outLen   输出   密文/明文存储空间长度
     * @return 返回错误代码
     */
    public static native int comEncryptionProcEX(
            int comID,
            int keyIndex,
            int openSeal,
            byte[] inData,
            int inLen,
            byte[] outData,
            int[] outLen
    );

}

1.4.3、直接调用

    /**
     * SM1对称加解密.
     *
     * @param keyIndex 容器号
     * @param openSeal 0 加密 1 解密
     * @param srcData  Base64编码待加密/解密数据
     * @return Base64编码加密/解密数据
     */
    public static void sm1CryptionProc(int keyIndex, int openSeal, String srcData) {

        byte[] inData = Base64Decoder.decode(srcData);
	//给足够大的空间
        byte[] outData = new byte[20000];
        int[] outDataLen = {20000};

        int n = Native.comEncryptionProcEX(0x00200000, keyIndex, openSeal, inData, inData.length, outData, outDataLen);
        if (n != 0) {
            System.out.println("失败");
        } else {
            System.out.println("成功,结果:" + Base64Encoder.encode(makeByteArray(outData, outDataLen[0])));
        }
    }



    /**
     * 拷贝有效数据.
     *
     * @param src    原数据
     * @param srcLen 原数据长度
     * @return 有效数据
     */
    public static byte[] makeByteArray(byte[] src, int srcLen) {

        byte[] finalData = new byte[srcLen];
        System.arraycopy(src, 0, finalData, 0, srcLen);

        return finalData;
    }

2、Linux环境

2.1、首先得到so库文件和头文件说明

2.2、根据头文件编写JNI文件(同上)

2.3、根据编写的JNI文件生成Java直接调用的so文件

2.3.1、生成 .o文件

执行命令: cc -fPIC -c -I . -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/linux" cn_bk_test_Native.c -pthread

2.3.2、生成 .so文件

执行命令: gcc -fPIC -shared -o [要生成的so文件]libcn_bk_test_Native.so [上一步生成的.o文件]cn_bk_test_Native.o [依赖的C程序so文件]test64.so -lpthread

2.4、编写最终调用的Java程序(同上)

三、C-JNI-JAVA类型对照表

C类型 JNI类型Java类型
unsigned charjbooleanBoolean
unsigned char*jbyteArraybyte[]
charjbyteByte
char*jstringString
unsigend shortjcharChar
shortjshortShort
intjintInteger
unsigned intjintint
unsigned int*jintArrayint[]
long longjlongLong
floatjfloatFloat
doublejdoubleDouble
void**&jintArrayint[]
SGD_UINT8*jbyteArraybyte[]
SGD_UINT32jintint
SGD_UINT32*jintArrayint[]
DWORDjintint

四、解决返回字符串乱码问题

详见:https://www.jianshu.com/p/4f38bd3bccc0

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java可以通过Java Native Interface(JNI调用C或C++编写的动态链接(也就是Windows下的.dll文件,Linux下的.so文件)。以下是一些简单的步骤: 1. 编写C或C++代码并将其编译为动态链接文件(.dll或.so文件)。 2. 在Java中使用JNI接口声明与C或C++代码中的函数对应的Java本地方法,并将其实现为Java本地方法。 3. 编译Java代码并将其打包成jar文件。 4. 将生成的动态链接文件放到Java程序能够访问到的目录下。 5. 运行Java程序。 以下是一个简单的示例: 1. 编写C代码 ```c #include <stdio.h> #include "jni.h" JNIEXPORT void JNICALL Java_com_example_Test_print(JNIEnv *env, jobject obj, jstring str) { const char *c_str = (*env)->GetStringUTFChars(env, str, NULL); printf("%s\n", c_str); (*env)->ReleaseStringUTFChars(env, str, c_str); } ``` 函数名必须以Java_开头,并加上Java类的完整路径和方法名。 2. 编译动态链接文件 假设我们已经将上述代码保存为test.c文件,可以使用以下命令将其编译为动态链接文件: - Windows:gcc -shared -o test.dll test.c -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" - Linux:gcc -shared -o libtest.so test.c -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" 注意:这里需要将JDK的include目录和平台相关的include目录添加到编译选项中。 3. 在Java中声明本地方法 ```java public class Test { static { System.loadLibrary("test"); // 加载动态链接文件 } public static native void print(String str); } ``` 4. 实现Java本地方法 ```java Test.print("Hello, world!"); // 调用本地方法 ``` 这样就可以在Java调用C代码了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值