JNI 技術實踐小結
昨天和一部zzz一起研究解決一個 java 調用第三方 dll 的問題,從零開始學習了 jni 技術的應用,現在總結如下。
事情的起因是一部的一個項目需要用到一個愛國者提供的基于 U 盤的加密技術。對方提供了 U 盤和一個 dll 動态連結庫 hiddenIO.dll 。在 U 盤的隐藏區域内可以儲存 USB-Key 資訊,通過這個 dll 裡的兩個方法可以使用 c/c++ 編寫程式在 U 盤的隐藏區域讀寫資訊,對方提供了示例代碼。由于一部的項目是基于 SWT/RCP 技術的,是以需要在 java 程式中調用這兩個方法。
目前 java 與 dll 互動的技術主要有 3 種: jni , jawin 和 jacob 。 Jni ( Java Native Interface )是 sun 提供的 java 與系統中的原生方法互動的技術(在 windows\linux 系統中,實作 java 與 native method 互調)。目前隻能由 c/c++ 實作。後兩個都是 sourceforge 上的開源項目,同時也都是基于 jni 技術的 windows 系統上的一個應用庫。 Jacob ( Java-Com Bridge )提供了 java 程式調用 microsoft 的 com 對象中的方法的能力。而除了 com 對象外, jawin ( Java/Win32 integration project )還可以 win32-dll 動态連結庫中的方法。就功能而言: jni >> jawin>jacob ,其大緻的結構如下圖:
jni 技術體系功能結構圖
就易用性而言,正好相反: jacob>jawin>>jni 。
Jvm 封裝了各種作業系統實際的差異性的同時,提供了 jni 技術,使得開發者可以通過 java 程式(代碼)調用到作業系統相關的技術實作的庫函數,進而與其他技術和系統互動,使用其他技術實作的系統的功能;同時其他技術和系統也可以通過 jni 提供的相應原生接口開調用 java 應用系統内部實作的功能。
在 windows 系統上,一般可執行的應用程式都是基于 native 的 PE 結構, windows 上的 jvm 也是基于 native 結構實作的。 Java 應用體系都是建構于 jvm 之上。
Windows 系統上的 java 體系
Jni 對于應用本身來說,可以看做一個代理模式。對于開發者來說,需要使用 c/c++ 來實作一個代理程式( jni 程式)來實際操作目标原生函數, java 程式中則是 jvm 通過加載并調用此 jni 程式來間接地調用目标原生函數。
Jni 調用過程示意圖
Jni 程式開發的一般操作步驟如下:
1. 編寫 java 中的調用類
2.用 javah 生成 c/c++ 原生函數的頭檔案
3. c/c++ 中調用需要的其他函數功能,實作原生函數 ( 原則上可以調用任何資源 )
4. 将項目依賴的所有原生庫和資源加入到 java 項目的 java.library.path
5. 生成 java 程式
6. 釋出 java 應用和 dll 庫
Jni 程式開發示例:
1.在 eclipse 項目中建立一個 TestHello.java ,輸入以下内容:
public class TestHello {
static {
System.loadLibrary("TestHello");
}
public static native void hello(String msg);
public static void main(String[] args) {
hello("Hello,Kimm!");
}
}
編譯生成 TestHello. class 檔案。
2. 在指令行下使用 javah TestHello 指令,生成 TestHello.h 頭檔案(就是 jni 代理 stub 的接口)。
3. 在 VC6 中建立一個項目 TestHello, 項目類型為 Win32 Dynamic-Link Library 。點選 OK 。
在彈出的視窗中選擇 A simple DLL project ,點選 Finish 。
打開項目所在的檔案目錄,将步驟 2 中生成的 TestHello.h 檔案複制到此目錄。點選左邊中間的 FileView ,切換到檔案浏覽模式。在 Header Files 上點選右鍵,選擇 Add Files to Folder… 。
選擇 TestHello.h 檔案,點選 OK 。
打開 StdAfx.h 檔案,再最後面添加:
#include <jni.h>
#include "TestHello.h"
打開 TestHello.cpp 檔案,在最後面添加一段代碼:
JNIEXPORT void JNICALL Java_TestHello_hello(JNIEnv * env, jclass obj, jstring jMsg){
const char *strMsgPtr = env->GetStringUTFChars( jMsg , 0);
MessageBox( 0, strMsgPtr,"Message box from VC++ ", 0 );
env->ReleaseStringUTFChars( jMsg, strMsgPtr);
}
在 VC 的菜單上選擇 Tools-Options… ,打開選項對話框。在 Directories 檔案夾,添加上 jdk 所在檔案夾下的 include 和 include\win32 檔案夾。
點選 VC 上的菜單項 Build-Build All ,生成 TestHello.dll 。
4.将 VC 項目 Debug 檔案夾中的 TestHello.dll 複制到 TestHello.class 所在的檔案夾下。
5. 在指令行下輸入 java TestHello ,彈出 MessageBox 對話框。調用 win32 api 成功。
樓主寫的不錯,我安樓主寫的走了一篇,對jni有了初步的了解,謝謝樓主。
我的用的VS 2005以下是我走的過程中,碰到的問題我解決的方法。希望對其他vs不熟的人有點幫助:
問題 1 : fatal error C1083: 無法打開包括檔案L .......... No such file or directory
解決:“項目--->屬性---->配置屬性---->C/C++---->正常---->附加包含目錄”選項中有很多include下的庫
問題 2: error C2664: “MessageBoxW”: 不能将參數 2 從“const char *”轉換為“LPCWSTR”
解決:所說字元集問題,我的vs2003這樣設: 項目屬性-> 配制屬性-> 正常-> 字元集-> 選:使用多位元組字元集
問題 3:沒有找到MSVCR80D.dll,是以這個應用程式未能啟動。
找到你的工程的檔案夾,如(myproject),找到其下的myproject\myproject\Debug\ myproject.rec,把它删掉(删掉整個Debug目錄也可以),重新編譯,搞定!
:lol: