1. 程式人生 > >java呼叫C/C++生成的dll動態連結庫----藉助JNI

java呼叫C/C++生成的dll動態連結庫----藉助JNI

由於專案的需要,最近研究了java 呼叫DLL的方法,將如何呼叫的寫於此,便於日後查閱:

採用的方法是JNI:Java Native Interface,簡稱JNI,是Java平臺的一部分,可用於讓Java和其他語言編寫的程式碼進行互動。

下面是從網上摘取的JNI工作示意圖:

總體說明:先在JAVA中建立一個類,通過javac生成.class,再由javah生成.h;然後將.h複製到VC下,由VC實現具體函,

並編譯通過後生成DLL,將DLL放入JAVA工程中使用,完畢。

下面說說具體步驟(含例項):

1、建java類:裝載DLL,宣告要使用DLL方法,具體實現由DLL負責;程式碼如下:

public class Java2cpp
{
  static 
  { 
    System.loadLibrary("javaCallcpp"); 
  } 

  public native int DLL_ADD(int a,int b);    //加
  public native int DLL_SUB(int a,int b);    //減
  public native int DLL_MUL(int a,int b);    //乘
  public native int DLL_DIV(int a,int b);     //除

  public static void main(String args[])
  {

    int sum = 0;

    Java2cpp test = new Java2cpp();

    sum = test.DLL_ADD(2, 4);
    System.out.println("Java call cpp dll result:" + sum);
  }

}

2、生成.h檔案:cmd 到Java2cpp.java目錄下,做如下操作:

  第一步:javac  -encoding  UTF-8   Java2cpp.java  生成java2cpp.class

  第二步:javah Java2cpp  生成Java2cpp.h標頭檔案,內容如下:

注意
:1、Java2cpp.h這個標頭檔案的內容是不能修改的,否則會出錯。
2、在javah命令操作時,在src或者bin下,比如:javah -classpath  .  -jni   testDLL.Java2cpp 中間是有個點的。 (目錄層級是用"."而不是"\")

3、製做VC動態庫:建立一個C/C++動態庫工程,命名為javaCallcpp,匯入java2cpp.h並實現其方法:

#include "Java2cpp.h"
#include "dllApi.h"

JNIEXPORT jint JNICALL Java_Java2cpp_DLL_1ADD(JNIEnv *env, jobject obj, jint a, jint b)
{
  int var = 0;

  var = DLL_API_ADD(a,b);

  return var;
}

JNIEXPORT jint JNICALL Java_Java2cpp_DLL_1SUB(JNIEnv *env, jobject obj, jint a, jint b)
{
  int var = 0;

  var = DLL_API_SUB(a,b);

  return var;
}

JNIEXPORT jint JNICALL Java_Java2cpp_DLL_1MUL(JNIEnv *env, jobject obj, jint a, jint b)
{
  int var = 0;

  var = DLL_API_MUL(a,b);

  return var;
}

JNIEXPORT jint JNICALL Java_Java2cpp_DLL_1DIV(JNIEnv *env, jobject obj, jint a, jint b)
{
  int var = 0;

  var = DLL_API_DIV(a,b);

  return var;
}  //此檔案完

加DLL_API_ADD()、減DLL_API_SUB()、乘DLL_API_MUL()、除DLL_API_DIV()四個函式在別一個

檔案中實現,檔名是dllApi.cpp,實現如下:

int DLL_API_ADD(int a,int b)
{
  return (a+b);
}

int DLL_API_SUB(int a,int b)
{
  return (a-b);
}

int DLL_API_MUL(int a,int b)
{
  return (a*b);
}

int DLL_API_DIV(int a,int b)
{
  return (a/b);
}  //此檔案完

此時工程還編譯不過,因為include<jni.h>出錯,需新增JNI所在的目錄,如下:

4、編譯動態庫工程:生成javaCallcpp.dll,並將這個動態庫複製到java工程目錄下:

5、使用DLL:執行java程式,結果如下:

至此,java呼叫dll已經完成。  *****************************************************分割線************************************************************************* 如果dll傳來的有中文情況,可能會出現亂碼。 解決策略為(在自己的專案中第二個方法即jstring WindowsTojstring(JNIEnv* env, const char* str) 是可以用的): #include "getChinese.h" #include "testDLL_Java2cpp.h" #include <Windows.h> char* jstringToWindows(JNIEnv *env, jstring jstr); jstring WindowsTojstring(JNIEnv* env, const char* str); JNIEXPORT jstring JNICALL Java_testDLL_Java2cpp_getResult (JNIEnv *env, jobject obj, jstring name) {     string str = "hello 你好啊,這是一個測試的dll";     const char* result = str.c_str();        jstring js = WindowsTojstring(env,result);     return js;     /*     if ( r!= NULL){         return env->NewStringUTF(r);         cout << r << endl;     }     else{         //return NULL;         return env->NewStringUTF(result);     }*/ } char* jstringToWindows(JNIEnv *env, jstring jstr) { //UTF8/16轉換成gb2312     int length = (env)->GetStringLength(jstr);     const jchar* jcstr = (env)->GetStringChars(jstr, 0);     int clen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, NULL, 0, NULL, NULL);     char* rtn = (char*)malloc(clen); //更正。作者原來用的是(char*)malloc( length*2+1 ),當java字串中同時包含漢字和英文字母時,所需緩衝區大小並不是     2倍關係。     int size = 0;     size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn, clen, NULL, NULL);     if (size <= 0)         return NULL;     (env)->ReleaseStringChars(jstr, jcstr);     rtn[size] = 0;     return rtn; } jstring WindowsTojstring(JNIEnv* env, const char* str) {//gb2312轉換成utf8/16     jstring rtn = 0;     int slen = strlen(str);     unsigned short * buffer = 0;     if (slen == 0)         rtn = (env)->NewStringUTF(str);     else     {         int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, NULL, 0);         buffer = (unsigned short *)malloc(length * 2 + 1);         if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length) >0)             rtn = (env)->NewString((jchar*)buffer, length);     }     if (buffer)         free(buffer);     return rtn; } 不管將c++的程式碼檔案如何設定為utf-8 還是會出現中文亂碼,所以採用上述的編碼方式可以解決這個問題。 *****************************************************貼出java側的輸出結果*********************************** package testDLL; public class Java2cpp { static {            System. loadLibrary("callDLL");      } public native String getResult(String in); public static void main(String[] args) { // TODO Auto-generated method stub            String result = "";            Java2cpp test = new Java2cpp(); result = test.getResult( "李海濤");//這裡的輸入有點多餘了            System. out.println( result);      } }