1. 程式人生 > >使用JNA替代JNI呼叫本地方法

使用JNA替代JNI呼叫本地方法

 JNA全稱是Java Native Access,是Sun推出的一種呼叫本地方法技術,比起它的同門師兄JNI,JNA大大簡化了呼叫本地方法的過程,使用也比較方便, JNA是在JNI的基礎上完善的,用青出於藍而勝於藍來形容一點不為過,下面看一下JNI的呼叫過程:    


      

      使用JNI你得完成上面這些步驟,比較麻煩,而是用JNA就省事多了,基本上不需要脫離Java環境就可以完成。
  
   JNA專案主頁是https://jna.dev.java.net/, 目前最新的版本是3.2.4 。下載時記得將自帶的Example.jar也下載下來,這個裡面提供了一些JNA的例子,通過這個能夠更快的瞭解JNA。

   使用JNA的呼叫本地方法的時候需要自定義資料結構,下面我們通過呼叫Windows提供的的鎖定工作站方法來了解一下JNA。

    1、首先查詢Windows API知道鎖定工作站的方法在user32.dll中定義,接下來定義一個介面來繼承JNA的Library.java介面,用作宣告DLL庫檔案,這裡我們就把它命名為User32:      

 public interface User32 extends Library {}

    2查詢user32.dll提供的API得知鎖定工作方法是LockWorkStation,返回型別是boolean型,在User32.java中新增相應的方法:

boolean LockWorkStation();

         這樣我們的User32.java這個類就定義好了。接下來我們寫測試程式進行呼叫。

    3、編寫測試類比如LockWorkStation.java,首先通過JNA的Native類載入對應的dll:     

User32 user32 = (User32) Native.loadLibrary("user32", User32.class
);

        然後就可以呼叫LockWorkStation方法了,完整程式碼如下:      

public class LockWorkStation {
    public static void main(String[] args) {
       User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
       user32.LockWorkStation();
    }
}

  這裡說明一下loadLibrary方法中第一個引數是需要載入的dll檔名稱,第二個引數的作用是讓JNA使用這個類的載入器去載入DLL檔案,載入順序是,先從Users.class類的當前資料夾找,如果沒有找到,再在工程當前資料夾下面找win32/win64資料夾,找到後搜尋對應的dll檔案,如果找不到再到WINDOWS下面去搜索,再找不到就會拋異常了。以TWAINDSM.dll將檔案放到工程的根資料夾可以按照下面這個格式放:

   上面的User32定義的是dll庫檔案,有時會碰到比如HANDLE、POINT、WORD和MSG等資料型別,有些資料型別JNA中沒有提供,需要自己定義,根據作用的不同,定義的時候繼承的父類也不一樣,比如HANDLE定義方法是:
 class HANDLE extends PointerType {
        private boolean immutable;
        public HANDLE() { }
        public HANDLE(Pointer p) { setPointer(p); immutable = true; }
       public Object fromNative(Object nativeValue, FromNativeContext context) {
            Object o = super.fromNative(nativeValue, context);
            if (INVALID_HANDLE_VALUE.equals(o))
                return INVALID_HANDLE_VALUE;
            return o;
        }
        public void setPointer(Pointer p) {
            if (immutable)
                throw new UnsupportedOperationException("immutable reference");
            super.setPointer(p);
        }
    }
    HANDLE被定義為型別安全的指標。而POINT用作表示座標,不需要這麼複雜,定義方式為:
 class POINT extends Structure {
        public int x, y;
        public POINT() { }
        public POINT(int x, int y) { this.x = x; this.y = y; }
  }
   
     使用JNA的過程中也不一定會一帆風順,比如會丟擲”非法記憶體訪問”,這時候檢查一下變數是否==null。還有記憶體對齊的問題,當從記憶體中獲取圖片資訊進行儲存的時候,如果記憶體對齊處理不好,就會丟擲很嚴重的異常,導致JVM異常退出,JNA提供了四種記憶體對齊的方式,分別是:ALIGN_DEFAULTALIGN_NONEALIGN_GNUCALIGN_MSVCALIGN_DEFAULT採用平臺預設的對齊方式(推薦);ALIGN_NONE是不採用對齊方式;ALIGN_GNUC為針對linux/gcc作業系統的對齊方式。ALIGN_MSVC為針對win32/msvc架構的記憶體對齊方式。

     JNA也提供了一種保護機制.比如防止JNA出現異常不會導致JVM異常退出,預設是開啟這個功能的,開啟方式為System.setProperty(“jna.protected”,”true”); 記得要在JNA載入dll檔案之前呼叫,然後try {...} catch(Throwable e)異常,不過你也不要期望過高,不要以為加上這個就萬事大吉,出現”非法記憶體訪問”的時候還是會束手無策。JNA也提供了一種保護機制.比如防止JNA出現異常不會導致JVM異常退出,預設是開啟這個功能的,開啟方式為System.setProperty(“jna.protected”,”true”); 記得要在JNA載入dll檔案之前呼叫,然後try {...} catch(Throwable e)異常,不過你也不要期望過高,不要以為加上這個就萬事大吉,出現”非法記憶體訪問”的時候還是會束手無策。