歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java如何調用C++編寫的dll(動態連接庫)

JNI的簡單使用

以一個最簡單的HelloWorld程序來介紹一下JNI的最基本的使用方法:

1)首先要有一個HelloWorld.java。
這個是主文件,裡面包括本地方法的java聲明,一個main函數,還有一個靜態代碼段,用來導入所需要的動態連接庫(在Wndows裡是.dll)。

代碼如下

//HelloWorld.java
class HelloWorld {
    public native void displayHelloWorld();//注意關鍵字native,這就說明這個方法是用本地方法實現的。

    static {//靜態代碼段裡面導入了hello.dll。
        System.loadLibrary("hello");
    }
   
    public static void main(String[] args) {//調用本類的displayHelloWorld方法,(當然了方法實際上是用c語言實現的)
        new HelloWorld().displayHelloWorld();
    }
}

2)編譯HelloWorld.java。

使用語句為:
javac HelloWorld.java


3)使用javah命令生成一個.h文件。

使用語句為:
javah HelloWorld

  這就是實現displayHelloWorld()方法的c文件的頭文件。文件名為HelloWorld.h代碼如下:
    /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:    HelloWorld
 * Method:    displayHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
可以看到,這個文件裡面主要就是需要在c文件裡面實現的方法的方法聲明。這個聲明和java文件HelloWorld.java的有一點區別,原來的方法不帶參數,可是現在有了兩個參數。
這兩個是任何一個本地方法都必須有的參數。
第一個參數是JNIEnv*,它用於連接從java應用程序傳給你的本地方法的參數和對象。第二個參數是一個jobject,它指向當前對象本身,你也可以把它理解為java裡面的this變量。對於一個本地實例方法,比如這個例子裡的displayHelloWorld方法,jobject參數就是一個對象當前實例的引用。對於本地類的方法,這個參數就是一個方法類的引用。在這個例子裡面不需要使用這兩個參數。

另外一點,可以發現方法的名稱和java文件裡的不一致,這個方法名由以下幾部分組成:
java_[包名+]類名_java方法名

4)編寫實現本地方法的c文件
 //本例中起名為HelloWorldImp.c
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>

JNIEXPORT void JNICALL 
Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj) 
{
    printf("Hello world!
");//這個例子中只輸出一行Hello World!
    return;
}

5)建立動態連接庫
    在Wndows下面使用下面的語句:
cl -Id:jdk1.3.1include -I d:jdk1.3.1includewin32  -LD HelloWorldImp.c -Fehello.dll
        這裡面有幾部分。D:jdk1.3.1是本地的java home的路徑。在include和includewin32目錄下面有產生動態連接庫需要的幾個.h文件,包括jni.h(在所有的實現native方法的c文件裡面都要include這個文件)等等。
        將產生的.dll文件放到環境變量path能找到的目錄下。現在運行命令:
        java HelloWorld
        就會看到如下輸出:
        Hello World!

//**********************************************

編寫高質量代碼 改善Java程序的151個建議 PDF高清完整版 http://www.linuxidc.com/Linux/2014-06/103388.htm

Java 8簡明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm

Java對象初始化順序的簡單驗證 http://www.linuxidc.com/Linux/2014-02/96220.htm

Java對象值傳遞和對象傳遞的總結 http://www.linuxidc.com/Linux/2012-12/76692.htm

Java對象序列化ObjectOutputStream和ObjectInputStream示例 http://www.linuxidc.com/Linux/2012-08/68360.htm

還有, 以下載自《JAVA如何調用C/C++方法》--acute(原作)

JAVA通過JNI調用本地C語言方法

JAVA以其跨平台的特性深受人們喜愛,而又正由於它的跨平台的目的,使得它和本地機器的各種內部聯系變得很少,約束了它的功能。解決JAVA對本地操作的一種方法就是JNI。

JAVA通過JNI調用本地方法,而本地方法是以庫文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX機器上是SO文件形式)。通過調用本地的庫文件的內部方法,使JAVA可以實現和本地機器的緊密聯系,調用系統級的各接口方法。

簡單介紹及應用如下:

一、JAVA中所需要做的工作

在JAVA程序中,首先需要在類中聲明所調用的庫名稱,如下:

static {

      System.loadLibrary(“goodluck”);

}

在這裡,庫的擴展名字可以不用寫出來,究竟是DLL還是SO,由系統自己判斷。

還需要對將要調用的方法做本地聲明,關鍵字為native。並且只需要聲明,而不需要具體實現。如下:

public native static void set(int i);

public native static int get();

然後編譯該JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就會生成C/C++的頭文件。

 例如程序testdll.java,內容為:

public class testdll

{

      static

      {

              System.loadLibrary("goodluck");

      }

       

      public native static int get();

      public native static void set(int i);

       

      public static void main(String[] args)

      {

              testdll test = new testdll();

              test.set(10);

              System.out.println(test.get());

      }

}

用javac testdll.java編譯它,會生成testdll.class。

再用javah testdll,則會在當前目錄下生成testdll.h文件,這個文件需要被C/C++程序調用來生成所需的庫文件。

二、C/C++中所需要做的工作

對於已生成的.h頭文件,C/C++所需要做的,就是把它的各個方法具體的實現。然後編譯連接成庫文件即可。再把庫文件拷貝到JAVA程序的路徑下面,就可以用JAVA調用C/C++所實現的功能了。

接上例子。我們先看一下testdll.h文件的內容:

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

#include <jni.h>

/* Header for class testdll */

 

#ifndef _Included_testdll

#define _Included_testdll

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:    testdll

 * Method:    get

 * Signature: ()I

 */

JNIEXPORT jint JNICALL Java_testdll_get

  (JNIEnv *, jclass);

 

/*

 * Class:    testdll

 * Method:    set

 * Signature: (I)V

 */

JNIEXPORT void JNICALL Java_testdll_set

  (JNIEnv *, jclass, jint);

 

#ifdef __cplusplus

}

#endif

#endif

在具體實現的時候,我們只關心兩個函數原型

JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);

這裡JNIEXPORT和JNICALL都是JNI的關鍵字,表示此函數是要被JNI調用的。而jint是以JNI為中介使JAVA的int類型與本地的int溝通的一種類型,我們可以視而不見,就當做int使用。函數的名稱是JAVA_再加上java程序的package路徑再加函數名組成的。參數中,我們也只需要關心在JAVA程序中存在的參數,至於JNIEnv*和jclass我們一般沒有必要去碰它。

好,下面我們用testdll.cpp文件具體實現這兩個函數:

#include "testdll.h"

int i = 0;

JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)

{

      return i;

}

JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)

{

      i = j;

}

編譯連接成庫文件,本例是在WINDOWS下做的,生成的是DLL文件。並且名稱要與JAVA中需要調用的一致,這裡就是goodluck.dll

把goodluck.dll拷貝到testdll.class的目錄下,java testdll運行它,就可以觀察到結果了。

另外:
使用Java直接調用VB編寫的DLL是不行的,因為VB根本不能生成標准的DLL動態鏈接庫,VB制作的DLL都是基於COM的動態鏈接庫,而不是直接輸出函數。

一般的做法使用C/C++對COM DLL進行二次封裝,將其中的功能已C API的形式倒出。

Copyright © Linux教程網 All Rights Reserved