從Java中引用DLL代碼
列表四演示代碼引用DLL函數
public interface CLibrary extends Library {
CLibrary INSTANCE1 = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "nativecode" : "c"),
CLibrary.class);
int helloWorld(int divider);
}
public static void main(String[] args) {
CLibrary.INSTANCE1.helloWorld(77));
}
列表四,訪問DLL函數
在列表四中,CLibrary實例被創建。這個對象允許指定的DLL被下載。接下來,是庫的裝入過程,標志 需要從庫中標記——在列表四的實例中,只有一個標志,被稱為helloWorld()。
列表五演示的程序來自列表四的代碼
C:\jnacode>java HelloWorld
Value is 1
列表五,調用的DLL的代碼
在列表五中沒有什麼好驚奇的——值77獲准進入函數。緊接著在函數內部,參數(77)被77除,得出答 案:1。
當我嘗試著著手解決DLL的問題,與調用約定聯系起來,我想看看生成DLL的過程。幸運的是,你可以 通過一種工具實現這個願望,這個工具被稱為Dependency Walker,通過Dependency Walker你可以看到 DLL的生成過程。為了實現,你需要下載一個免費的Dependency Walker副本,打開,然後把DLL裝載在裡 面。你就可以看見如圖一所示的類似內容。
圖一,DLL內部信息
注意圖一中的函數名稱,這些函數名稱與DLL標志helloWorld()相匹配。如果當你創建一個DLL的時候 ,使用標准調用約定,函數名稱將會如圖2所示。
圖2,標准調用約定
注意函數名稱變化的方式。現在,如果你嘗試運行Java程序,將會得到令人厭煩的錯誤提示,如列表 六所示。
C:\jnacode>java HelloWorld
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'helloWorld': The specified procedure could not be found.
at com.sun.jna.Function. (Function.java:129)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:250)
at com.sun.jna.Library$Handler.invoke(Library.java:191)
at $Proxy0.helloWorld(Unknown Source)
at HelloWorld.main(HelloWorld.java:31)
列表六,連接器錯誤
包括上面的錯誤情況,因為我自己也突然遇到了以上情形。所以,這是一個完全的JNA現場演示案例。 這與JNI是如何聯系的——JNA的前身?
JNI:不是今天,謝謝!
我經常想到,這些年來JNI承受了很多評論。在很多實例中,一個軟件支持部門要做的第一件事情就是 說:“你使用JNI嗎?”如果答案是肯定的,然後在很多例子中,沒有接踵而來的支持。另一方面,在很多 顧問工作中,我看見很多JNI案例,這些JNI案例在大型Java和C++代碼基礎之間使用。在這樣一種情況中 ,Java和C++通過所有的單元,結合測試,但是生產出來的代碼隨時有崩潰的可能性。這種情況下,Java 和C++程序員嘗試著掩飾這種情況,每個人都在指責對方。
解決JNI的悲哀的一個比較好的方案是通過一系列清單項目運行,這些在任何一邊的清單項目都是為了 代碼。舉個例子,在C這邊的代碼:
· 一個數組邊界被突破?
· 一個空指針被取消引用?
· 動態內存被正確的分配?
JNA有可能結束這種類型情況。
庫許可問題?
JNA令人感興趣的是它開啟了Java直接存儲DLL代碼的功能。它也適用於其它的庫技術,比如共享的 Unix庫。軟件組成部分得到許可的意思是什麼?JNA的使用也許是令人信服的,可以用來存取Java中的許可 的庫代碼。另一方面是JNA代碼可以允許存取安全限制庫代碼。
除了上面所考慮的問題,具有存取遺留代碼的能力也是很重要的。現在讓我們來看一看。
當遺留代碼調用通過JNA報錯時
回顧列表三中的代碼,除一個參數使之成為一個不變值。如果我把0設置為除數,執行操作的時候會發 生什麼那?
public interface CLibrary extends Library {
CLibrary INSTANCE1 = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "nativecode" : "c"),
CLibrary.class);
int helloWorld(int divider);
}
public static void main(String[] args) {
CLibrary.INSTANCE1.helloWorld(77));
System.out.println ("Value: " + CLibrary.INSTANCE1.helloWorld(0));
}
列表7把0設置為除數的情況
當我執行列表七中的代碼,我將會得到如列表八所示的輸出示意。
C:\jna_article\jnacode>java HelloWorld
Value is 1
#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_INT_DIVIDE_BY_ZERO (0xc0000094) at pc=0x009b1365, pid=1768, tid=458
4
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_17-b04 mixed mode)
# Problematic frame:
# C [nativecode.dll+0x11365]
#
# An error report file with more information is saved as hs_err_pid1768.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
列表8,障礙:除數為0的意外情況
這可不漂亮,但是我們還是得到了一些結果。無論如何,在現實生活中是線性增長的例子。想像一下 ,嘗試在一個大的代碼庫中查找的狀況。總的來說,JNA是很強大的,但是就像任何強大的事情一樣,它 還需要責任感。
運行提供的代碼以及CLASSPATH事務
伴隨著JNA,對此進行安排並且運行並不是十分困難——確認你跟隨了一些簡單的指導方針。我已經實 現了自己定制的DLL,並且編輯Java代碼作為源代碼的一部分。Zip文件。你需要確定這些代碼可以在 Windows XP上面運行,不需要編輯其它什麼部件。
運行代碼,解壓源文件到一個文件夾,比如C:\jnacode。接下來的步驟是可選擇的,因為源文件包含 一個jna.jar. 副本。如果你打算擁有自己的jna.jar. 副本,或者一個新的發布版本。然後再下載 jna.jar.文件的副本。為了使事情更為簡單,將jna.jar文件放到上面的文件夾中。然後,確認jna.jar和 包含文件夾都在CLASSPATH中。接下來,你可以嘗試運行HelloWorld類。
如果你感到充滿了刺激或者活力。你也許會喜歡嘗試著創建你自己的DLL,就像是我們在前面所描述的 。只需要按照上面所描述的做,你能做的很好。以防萬一,如果你遇到任何問題,你都可以拜訪JNA站點 。
結論:JNA是如何一步步走來的?
我很喜歡JNA。它非常的易於使用。如果你的庫代碼是使用正確的方法編輯的,你就不會遇到很多繁瑣 的問題。JNA即將成為非常有用的工具來組織處理大量的復雜的工作。關鍵業務遺留代碼。不再有厭煩的 JNI標記,並且頭文件可以自動生成。
有一點非常重要,與任何新技術相比,是非常容易把人們帶進麻煩中的。任何JNA都不例外。因為這個 原因,如果JNA有條不紊的使用,它將必定會超越JNI以及那些昂貴的私有的橋接機制。什麼樣的形式可能 會采取這樣的規則?JNA資源中的任何調用(比如庫代碼)將會被充分的記錄,提供審查索引。面向方面的技 術建議自己使用這種方案。比如說,EJB3攔截裝置將會被用於確定JNA代碼記錄的效果。
另外,任何數據獲准進入本地庫(通過JNA代碼)將會被認真的檢查。我猜想JNA成功的關鍵就是操作進 行的緩慢而認真。