运用jni实现java调用C++

本文详细介绍了如何使用JNI技术在Java中调用C++编写的动态库,包括Java类的声明、JNI头文件的生成、C++代码实现、动态库加载及错误处理。涉及到的关键点有:JNI关键字、数据类型转换、动态库路径、32位与64位兼容性等。
摘要由CSDN通过智能技术生成

1.      编写java类,用该类将DLL对外提供的函数服务进行声明,其中java方法均申明为native,其方法名可以自定义,函数体不用实现。具体代码如下:

package com.aizizai.calldll;

public class MyNative {

       public MyNative()

       {

             

       }

       static

       {

              System.loadLibrary("hellodll");

       }

       public native static void HelloWord();

       public native static int CheckSign(String cstrPubKey,String cstrSignResult,String encryptStr);

       public native static void get();

       public native static void set();

}

其中,hellodll为动态库的名称,不需要写扩展名。CheckSign函数的入参是三个字符串。

2.      用javah工具,通过java类生成.h头文件。这个.h头文件是C++动态库中的头文件,C++程序实现需要用到它。

1)        用javac MyNative.java 编译MyNative.class文件

2)        用javah MyNative编译出com_aizizai_calldll_MyNative.h文件,其内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_aizizai_calldll_MyNative */

 

#ifndef _Included_com_aizizai_calldll_MyNative

#define _Included_com_aizizai_calldll_MyNative

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    HelloWord

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_HelloWord

  (JNIEnv *, jclass);

 

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    CheckSign

 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I

 */

JNIEXPORT jint JNICALL Java_com_aizizai_calldll_MyNative_CheckSign

  (JNIEnv *, jclass, jstring, jstring, jstring);

 

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    get

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_get

  (JNIEnv *, jclass);

 

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    set

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_set

  (JNIEnv *, jclass);

 

#ifdef __cplusplus

}

#endif

#endif

JNIEXPORT、JNICALL等是jni的关键字。Jstring是jni特有的数据类型,介于java与C++的数据类型之间,jstring不能和char*等同,需要做一定的转换。这边补充一个jni数据类型jint,它对应java入参int类型,其无需转化,可以在C++中当int使用。

3.      编写C++代码实现.h头文件声明的函数,代码如下:

#include "stdafx.h"

#include "com_aizizai_calldll_MyNative.h"

 

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_HelloWord(JNIEnv *, jclass)

{

}

JNIEXPORT jint JNICALL Java_com_aizizai_calldll_MyNative_CheckSign(JNIEnv * env, jclass, jstring cstrPubKey, jstring cstrSignResult, jstring encryptStr)

{

       const char* cstrPubKey_c;

       const char* cstrSignResult_c;

       const char* encryptStr_c;

       cstrPubKey_c = env->GetStringUTFChars(cstrPubKey, false);//入参由jstring转成char

       cstrSignResult_c = env->GetStringUTFChars(cstrSignResult, false);

       encryptStr_c = env->GetStringUTFChars(encryptStr, false);

       //输出入参

       int num = MultiByteToWideChar(0,0, cstrPubKey,-1,NULL,0);

       wchar_t*widestr = new wchar_t[num];

       MultiByteToWideChar(0,0, cstrPubKey,-1,widestr,num);

       OutputDebugString(widestr);

delete []widestr;

      

       return 1;

}

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_get(JNIEnv *, jclass)

{

}

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_set(JNIEnv *, jclass)

{

}

编译后生成动态库,命名成hellodll,放入到java.library.path目录中。

4.      用java调用动态库输出结果:

public class DllTest {

      

       public static void main(String[] args) {

             

              String encryptStr = "";//

              String cstrSignResult = "";//

              String cstrPubKey ="";//

 

           MyNative myna = new MyNative();

              int a = myna.CheckSign(cstrPubKey,cstrSignResult,encryptStr);

              System.out.println(a);

       }

 

}

 

常见错误:

1.      Jni传递中文时乱码

Java内部使用的16bit的unicode编码(utf-16),中英文都是两个字节;jni使用的utf-8编码,utf-8是变长unicode编码,中文三个字节,ascii一个字节;C++汉字两个字节(GB2312),ascii一个字节。下面提供一个utf8转gb2312的方法:

char* U2G(const char* utf8)

{

       int len = MultiByteToWideChar(CP_UTF8,0,utf8,-1,NULL,0);

       wchar_t * wstr = new wchar_t[len +1];

       memset(wstr,0,len+1);

       MultiByteToWideChar(CP_UTF8,0,utf8,-1,wstr,len);

       len = WideCharToMultiByte(CP_ACP,0,wstr,-1,NULL,0,NULL,NULL);

       char * str = new char[len +1];

       memset(str,0,len+1);

       WideCharToMultiByte(CP_ACP,0,wstr,-1,str,len,NULL,NULL);

       if(wstr)

              delete [] wstr;

       return str;

}

2.      包含jni头文件

在jdk安装目录中找到jni的三个头文件C:\ProgramFiles\Java\jdk1.7.0_25\include下的jni.h;C:\Program Files\Java\jdk1.7.0_25\include\win32下:jawt_md.h、jni_md.h。然后放入到vs的include目录下,我本机目录是C:\ProgramFiles (x86)\Microsoft Visual Studio 10.0\VC\include。

 

3.      Javah编译.h文件时的路径问题

在javah编译.h文件时常会遇到如下错误:

正确方法是退至src根目录,然后在编译类前加上全路径。

常见错误如下:

4.      loadlibrary路径

获取java.library.path

System.out.println(System.getProperty("java.library.path"));

 

5.      32位dll不能在64位调用

编译出来的hellodll,被java调用时出错

解决方法:

修改vs的方案生成配置,“生成”->“配置管理器”->“活动解决方案平台”->“x64”,如果没有x64选项,则选择新建。

 

 

参考内容:

http://www.cnblogs.com/liuling/p/2013-12-20.html

http://www.cnblogs.com/babietongtianta/p/3143900.html

 

1.      编写java类,用该类将DLL对外提供的函数服务进行声明,其中java方法均申明为native,其方法名可以自定义,函数体不用实现。具体代码如下:

package com.aizizai.calldll;

public class MyNative {

       public MyNative()

       {

             

       }

       static

       {

              System.loadLibrary("hellodll");

       }

       public native static void HelloWord();

       public native static int CheckSign(String cstrPubKey,String cstrSignResult,String encryptStr);

       public native static void get();

       public native static void set();

}

其中,hellodll为动态库的名称,不需要写扩展名。CheckSign函数的入参是三个字符串。

2.      用javah工具,通过java类生成.h头文件。这个.h头文件是C++动态库中的头文件,C++程序实现需要用到它。

1)        用javac MyNative.java 编译MyNative.class文件

 

2)        用javah MyNative编译出com_aizizai_calldll_MyNative.h文件,其内容如下:

 

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_aizizai_calldll_MyNative */

 

#ifndef _Included_com_aizizai_calldll_MyNative

#define _Included_com_aizizai_calldll_MyNative

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    HelloWord

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_HelloWord

  (JNIEnv *, jclass);

 

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    CheckSign

 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I

 */

JNIEXPORT jint JNICALL Java_com_aizizai_calldll_MyNative_CheckSign

  (JNIEnv *, jclass, jstring, jstring, jstring);

 

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    get

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_get

  (JNIEnv *, jclass);

 

/*

 * Class:     com_aizizai_calldll_MyNative

 * Method:    set

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_set

  (JNIEnv *, jclass);

 

#ifdef __cplusplus

}

#endif

#endif

JNIEXPORT、JNICALL等是jni的关键字。Jstring是jni特有的数据类型,介于java与C++的数据类型之间,jstring不能和char*等同,需要做一定的转换。这边补充一个jni数据类型jint,它对应java入参int类型,其无需转化,可以在C++中当int使用。

3.      编写C++代码实现.h头文件声明的函数,代码如下:

#include "stdafx.h"

#include "com_aizizai_calldll_MyNative.h"

 

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_HelloWord(JNIEnv *, jclass)

{

}

JNIEXPORT jint JNICALL Java_com_aizizai_calldll_MyNative_CheckSign(JNIEnv * env, jclass, jstring cstrPubKey, jstring cstrSignResult, jstring encryptStr)

{

       const char* cstrPubKey_c;

       const char* cstrSignResult_c;

       const char* encryptStr_c;

       cstrPubKey_c = env->GetStringUTFChars(cstrPubKey, false);//入参由jstring转成char

       cstrSignResult_c = env->GetStringUTFChars(cstrSignResult, false);

       encryptStr_c = env->GetStringUTFChars(encryptStr, false);

       //输出入参

       int num = MultiByteToWideChar(0,0, cstrPubKey,-1,NULL,0);

       wchar_t*widestr = new wchar_t[num];

       MultiByteToWideChar(0,0, cstrPubKey,-1,widestr,num);

       OutputDebugString(widestr);

delete []widestr;

      

       return 1;

}

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_get(JNIEnv *, jclass)

{

}

JNIEXPORT void JNICALL Java_com_aizizai_calldll_MyNative_set(JNIEnv *, jclass)

{

}

编译后生成动态库,命名成hellodll,放入到java.library.path目录中。

4.      用java调用动态库输出结果:

public class DllTest {

      

       public static void main(String[] args) {

             

              String encryptStr = "";//

              String cstrSignResult = "";//

              String cstrPubKey ="";//

 

           MyNative myna = new MyNative();

              int a = myna.CheckSign(cstrPubKey,cstrSignResult,encryptStr);

              System.out.println(a);

       }

 

}

 

常见错误:

1.      Jni传递中文时乱码

Java内部使用的16bit的unicode编码(utf-16),中英文都是两个字节;jni使用的utf-8编码,utf-8是变长unicode编码,中文三个字节,ascii一个字节;C++汉字两个字节(GB2312),ascii一个字节。下面提供一个utf8转gb2312的方法:

char* U2G(const char* utf8)

{

       int len = MultiByteToWideChar(CP_UTF8,0,utf8,-1,NULL,0);

       wchar_t * wstr = new wchar_t[len +1];

       memset(wstr,0,len+1);

       MultiByteToWideChar(CP_UTF8,0,utf8,-1,wstr,len);

       len = WideCharToMultiByte(CP_ACP,0,wstr,-1,NULL,0,NULL,NULL);

       char * str = new char[len +1];

       memset(str,0,len+1);

       WideCharToMultiByte(CP_ACP,0,wstr,-1,str,len,NULL,NULL);

       if(wstr)

              delete [] wstr;

       return str;

}

2.      包含jni头文件

在jdk安装目录中找到jni的三个头文件C:\ProgramFiles\Java\jdk1.7.0_25\include下的jni.h;C:\Program Files\Java\jdk1.7.0_25\include\win32下:jawt_md.h、jni_md.h。然后放入到vs的include目录下,我本机目录是C:\ProgramFiles (x86)\Microsoft Visual Studio 10.0\VC\include。

 

 

3.      Javah编译.h文件时的路径问题

在javah编译.h文件时常会遇到如下错误:

 

 

正确方法是退至src根目录,然后在编译类前加上全路径。

常见错误如下:

 

4.      loadlibrary路径

获取java.library.path

System.out.println(System.getProperty("java.library.path"));

 

5.      32位dll不能在64位调用

编译出来的hellodll,被java调用时出错

 

解决方法:

修改vs的方案生成配置,“生成”->“配置管理器”->“活动解决方案平台”->“x64”,如果没有x64选项,则选择新建。

 

 

参考内容:

http://www.cnblogs.com/liuling/p/2013-12-20.html

http://www.cnblogs.com/babietongtianta/p/3143900.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值