Do you drink java?



                像寫情書一樣coding

           

          例解 VC++ 6.0 實現 JNI

          例解 VC++ 6.0 實現 JNI


          作者:陳健明

          作者簡介


          陳健明,華中師范大學網絡與通訊研究所,您可以通過chenjm2000@hotmail.com和作者取得聯系。

          內容摘要


          JNI是JDK的一部分,用于為Java提供一個本地代碼的接口。通過使用JNI編寫的程序能夠確保你的代碼能夠完全的移植到所有的平臺。JNI使得運行在JVM虛擬機上的Java代碼能夠操作使用其它語言編寫的應用程序和庫,比如C/C++以及匯編語言等。此外JNI提供的某些API還允許你把JVM嵌入到本地應用程序中。下圖表達了JNI所扮演的角色。

          本文將通過一個實例來闡述使用VC++6.0來實現JNI的完整過程。使用JNI來整合本地代碼和Java代碼的步驟是確定的,沒有再創作的余地,所以讀者可以通過本文的步驟來逐步認識到,其實Java也是"沒有什么不可以"的。

          一、JNI的實現


          任務描述:在Java中調用windows下的消息框函數,并且從Java中傳遞一個字符串作為MessageBox函數的顯示文本參數,顯示在消息框的中間。下面讓我們一起進入這一奇妙的旅程。

          Step 1:寫一個Java類,在這個類中包含了需要調用的本地方法的描述。

          //WinMsgBox.java
          package edu.netcom.jni;
          public class WinMsgBox  
          {
          	static{
          		System.loadLibrary("WinMsgDll");    // (1)
          	}
          	public native void showMsgBox(String str);  // (2)
          }
          

          (1)中WinMsgDll是動態鏈接文件的文件名,不用加擴展名,因為在不同的平臺下動態鏈接文件擴展名是不同的,由JVM自動識別,比如在Solaris下,會被轉換為WinMsgDll.so;而Win32環境下會轉換為WinMsgDll.dll。這個文件名必須和Step 4中生成的文件名一致。這個文件的存放位置也很重要,它只能被放在JVM屬性值java.library.path中指定的文件夾中。這個屬性值可以使用System.getProperty("java.library.path");來查看。一般情況下,至少放在這幾個位置是確定可靠的,windows安裝目錄下的system32下面,JDK安裝目錄下的bin下面,以及調用主類文件的當前目錄。

          (2)中指明了你必須用本地代碼實現的方法。

          Step 2:提示符下使用命令javac -d . WinMsgBox.java編譯Step 1編寫的java文件。

          此時會在當前目錄下建立一個edu\netcom\jni目錄結構,并且一個WinMsgBox.class文件存在其中。

          Step 3:提示符下使用命令javah -jni edu.netcom.jni.WinMsgBox,此時會在當前目錄下產生一個edu_netcom_jni_WinMsgBox.h文件,注意這個文件名是由(包名+類名)組成,中間用(_)隔開。此文件內容如下:

          /* DO NOT EDIT THIS FILE - it is machine generated */
          #include                        // (1)
          /* Header for class edu_netcom_jni_WinMsgBox */
          
          #ifndef _Included_edu_netcom_jni_WinMsgBox
          #define _Included_edu_netcom_jni_WinMsgBox
          #ifdef __cplusplus
          extern "C" {
          #endif
          /*
           * Class:     edu_netcom_jni_WinMsgBox
           * Method:    showMsgBox
           * Signature: (Ljava/lang/String;)V       // (2)
           */
          JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
            (JNIEnv *, jobject, jstring);           // (3)
          
          #ifdef __cplusplus
          }
          #endif
          #endif
          

          (1)包含的jni.h存在于JDK安裝目錄下的include下面。

          (2)(Ljava/lang/String;)V這是函數的標記符,當從本地方法端訪問Java端的方法時,會用到這個標記符。JNI中為每種數據類型也定義了標記符,標記符的規則請查看JNI標準文檔。

          (3)在WinMsgBox.java中本地方法void showMsgBox(String str);的定義,被映射為JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox(JNIEnv *, jobject, jstring); 其中函數名的映射規則是(Java_包名_類名_方法名),如果存在重載的方法,則在后面還會增加每個參數的標記符。每一個方法映射到本地C函數后都會增加兩個參數:JNIEnv *和jobject,關于這兩個參數的用法將在后面闡述。另外,所有Java中的數據類型都會按一定規則進行映射為本地數據類型,這些數據類型都是在jni.h中定義的。下面分別按照基本數據類型,和對象類型列出。

          表1 Java基本類型到本地類型的映射

          表2 Java中的類到本地類型的映射

          Step 4:使用VC來編寫本地方法的實現函數,最后編譯成.dll文件。過程如下:

          1) 選擇new->projects(選擇Win32 Dynamic-Link Library,以Step 1中指定的庫名WinMsgDll作為工程名)->OK->An ampty DLL project->Finish。

          2) 選擇Tools->Options->Directories(添加目錄D:\J2SDK1.4.2_03\INCLUDE和D:\J2SDK1.4.2_03\INCLUDE\WIN32)。在這些目錄中包含JNI所需的頭文件。

          3) 將Step 3生成的edu_netcom_jni_WinMsgBox.h拷貝到WinMsgDll工程文件夾中。然后FileView中添加這個頭文件。

          4) 添加源文件WinMsgDll.cpp,內容如下:

          #include "windows.h"
          #include "edu_netcom_jni_WinMsgBox.h"
          /*
           * Class:     edu_netcom_jni_WinMsgBox
           * Method:    showMsgBox
           * Signature: (Ljava/lang/String;)V
           */
          JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
          (JNIEnv * env, jobject obj, jstring str){
          	const char *msg;
          	msg = env->GetStringUTFChars(str,0);
          	MessageBox(NULL,msg,"Java invoke",MB_OK);
          	env->ReleaseStringUTFChars(str,msg);
          }
          

          5) 編譯生成WinMsgBox.dll文件。并將這個.dll文件拷貝到Step 1中說明的目錄中。

          注意:

          1) 我們知道dll文件有兩種指明導出函數的方法,一種是在.def文件中定義,另一種是在定義函數時使用關鍵字__declspec(dllexport)。而在JNI中函數定義中的關鍵字JNIEXPORT實際在jni_md.h中如下定義,#define JNIEXPORT __declspec(dllexport),可見JNI默認的導出函數使用第二種。使用第二種方式產生的導出函數名會根據編譯器發生變化,在有的情況下會發生找不到導出函數的問題(我們在JSP中使用JNI時就發生了這種問題,百思不得其解,后來強行加入一個.def文件就解決了)。因此最好是使用第一種方法自己定義一個.def文件來指明導出函數,這種情況下會強制使用第一種方式產生導出函數。本例中可以加入一個WinMsgDll.def文件,內容如下:

          LIBRARY      "WinMsgDll"
          DESCRIPTION  'message Windows Dynamic Link Library'
          EXPORTS
              ; Explicit exports can go here
          	Java_edu_netcom_jni_WinMsgBox_showMsgBox
          

          2) 從本例中,我們可以看到WinMsgBox.java決定了edu_netcom_jni_WinMsgBox.h,而后者又決定了WinMsgDll.dll,也就是說,這是一個"牽一發而動全身"的過程,如果你改動了WinMsgBox.java,就一定要把整個步驟都走一遍(這一點一定要切記,因為這也是我們跌得鼻青臉腫后才得出的警世良言)。

          3) 生成的.dll文件一定要正確拷貝到Step 1說明的目錄中,本例中是將生成的WinMsgDll.dll和Step 5中的測試文件放在同一個目錄下的(這也是我們困惑了很久才解決的問題)。

          Step 5:編寫一個測試文件來測試對WinMsgDll.dll的調用。測試文件TestJNI.java內容如下:

          //TestJNI.java
          import edu.netcom.jni.WinMsgBox;
          public class TestJNI 
          {
          	public static void main(String[] args) 
          	{
          		     WinMsgBox box = new WinMsgBox();
          		box.showMsgBox("Wonderful!!");
          	}
          }
          

          編譯,運行,windows下的對話框躍然屏幕中間。到此為此,整個JNI的實現過程就已經完成了。

          二、補充說明


          JNI為程序員提供了一種方法,使得他們能夠充分利用JVM以外的,完全由平臺決定的功能。但是你不應該濫用JNI。大多數情況可能是因為這樣而必須使用JNI,如你已經有做好了的本地.dll文件,其中已經包含了大量嘔心瀝血的函數,而你不愿意,也不可能完全用Java重寫它們,那么此時你應該用JNI。但是你必須按前面的步驟來進行,只是在Step 4中編寫的.dll要調用已有的本地.dll,也就是說用Step 4中的.dll"封裝"已有的.dll。

          在Java中定義的本地方法映射到本地C函數后都會增加兩個參數:JNIEnv *和jobject。第一個參數JNIEnv是一個指針,指向在jni.h中定義的一個數據結構,結構中包含了一系列的函數的指針,我們把它們稱之為JNI函數。使用這些JNI函數可以使程序員從本地函數這一側完成對Java的操作,如訪問Java字符串、數組、調用Java的方法、成員變量、甚至處理Java端的異常等。第二個參數會根據Java類中本地方法的定義不同而不同,如果是定義為static方法,類型會是jclass,表示對特定Class對象的引用,如果是非static方法,類型是jobject,表示當前對象的引用(本例中就是對WinMsgBox對象的引用),相當于this。

          C函數通過JNI接受來自Java的傳參也是按基本類型直接傳值,對象傳引用。對于基本類型可以按照表1來使用,其它類型都必須使用JNI函數進行轉換后才能在C函數中使用。如本例中從Java中傳遞了一個字符串,映射為JNI類型jstring。使用JNI函數GetStringUTFChars將jstring轉換為UTF-8字符串,然后便可以使用C語言中的任何字符串操作函數進行操作。由于JVM在調用本地方法時,是在虛擬機中開辟了一塊本地方法棧供本地方法使用,當本地方法使用完UTF-8串后,必須使用ReleaseStringUTFChars,通過它來通知虛擬機去回收UTF-8串占用的內存,否則將會造成內存泄漏,最終導致系統崩潰。同樣的,如果需要將C函數返回的返回值能夠正確通過JNI傳給Java,也要使用JNI函數轉換為前面兩個表中的類型。

          關于 JNI 函數的權威文檔請參閱 Java Native Interface 1.5 Specification

          關于 JNI 函數的教程請參閱 Java Native Interface Tutorial

          posted on 2005-07-24 10:17 leon 閱讀(1216) 評論(2)  編輯  收藏 所屬分類: JNI

          評論

          # re: 例解 VC++ 6.0 實現 JNI 2006-04-26 14:03 jd

          為什么我將類打包后會出現錯。而且我重新做了一遍dll!  回復  更多評論   

          # re: 例解 VC++ 6.0 實現 JNI 2006-06-03 15:04 leon

          先檢查一下dll放置的路徑咯  回復  更多評論   


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          導航

          統計

          公告

          DSC_0106.jpg
          本博客已經搬家到CSDN
          http://blog.csdn.net/chenweionline




          常用鏈接

          留言簿(5)

          隨筆分類

          隨筆檔案

          About Technology

          My Favorite Website

          搜索

          積分與排名

          最新評論

          • 1.?re: 胃痙攣
          • 兄弟們你們都做胃鏡了嗎開的啥藥?有什么養胃秘方啊?請賜教啊!
          • --我也進來了
          • 2.?re: 胃痙攣
          • @痛啊
            我感覺比生孩子還疼,生孩子還能有意識呢!這種疼還叫不出來連說話的力氣都沒了
          • --我也進來了
          • 3.?re: 胃痙攣
          • 評論內容較長,點擊標題查看
          • --我也進來了
          • 4.?re: 胃痙攣
          • 我都周期性的發作,算來有4年了 。每年最少一次,一次最少2天。吃了好多藥就差沒去做胃鏡了。 剛剛發作就隨便煮了點姜湯喝下就好了些,不知道還會不會發作了。
          • --許家洛
          • 5.?re: 胃痙攣
          • 我昨晚也疼的要死。。上次疼的直接120送醫院了,一上120我竟然就不疼了,醫院里掉了瓶鹽水就放回家了。這次正好國外旅游中,幾乎疼了一整晚,整個在床上翻滾狀態了。今天不疼了,就是整個人難受。。。求助阿
          • --胃疼
          • 6.?re: [Swing]在 JFileChooser 中進行文件驗證的小技巧
          • 輸入*還是會有問題
          • --ngh
          • 7.?re: 胃痙攣[未登錄]
          • 昨晚一夜的身不如死的感覺,胃痙攣兄不要來了吧 ,求你了
          • --李飛
          • 8.?re: 胃痙攣
          • 評論內容較長,點擊標題查看
          • --daidai
          • 9.?re: 胃痙攣
          • 評論內容較長,點擊標題查看
          • --痛啊
          • 10.?re: 胃痙攣
          • 媽啊 痛的快死了~!第一次感覺過這樣的痛啊~!和女的生孩子有的一比了吧~!哭哭哭~!
          • --痛啊

          閱讀排行榜

          主站蜘蛛池模板: 呈贡县| 温州市| 张家港市| 林西县| 衡阳市| 土默特左旗| 海原县| 黄平县| 思南县| 信丰县| 新安县| 隆林| 西林县| 宁海县| 汉源县| 沙坪坝区| 开封市| 三明市| 惠来县| 榆树市| 合川市| 石家庄市| 滕州市| 轮台县| 含山县| 黄石市| 泸水县| 伊吾县| 新民市| 铜鼓县| 河池市| 美姑县| 金堂县| 梁平县| 龙山县| 广州市| 凤台县| 上虞市| 周宁县| 宁化县| 桓仁|