2012-08-24

Hello JNative 1.4 RC3

聽說 Java 可以呼叫 DLL 檔,這就來玩玩看。

官方用法是 Java Native Interface(JNI),但是會牽扯到非 Java 的程式,還是先跳過好了。

找到一些 3rd-party library 可以用,包括 Jawin、Jacob 與 JNative,試過之後,發現 JNative 比較簡單用。

先去下載 JNative_1.4RC3_bin,解開後裡面只有一個 JNative.jar 檔,不像其他網路文章說得有二到三個檔案,包括一個 jar 檔、一個 Windows 用的 dll 檔和一個 Linux 用的 so 檔,這類 3rd-party library 運作邏輯大概是這樣呼叫流程:
Your Java Code -> JNative.jar -> JNative.dll -> Your DLL

那新版的 JNative.dll 在哪?解開 JNative.jar 裡面有個 lib-bin 就可以看到 dll 和 so。

先用網路上的範例試試看:
public class SystemTime extends AbstractBasicData {

  public short wYear;
  public short wMonth;
  public short wDayOfWeek;
  public short wDay;
  public short wHour;
  public short wMinute;
  public short wSecond;
  public short wMilliseconds;

  /**
   * 分配内存,并返回指针
   */
  public Pointer createPointer() throws NativeException {
    pointer = new Pointer(MemoryBlockFactory.createMemoryBlock(getSizeOf()));
    return pointer;
  }

  /**
   * 内存大小
   */
  public int getSizeOf() {
    return 8 * 2;
  }

  /**
   * 获取通过内存指针解析出结果
   */
  public SystemTime getValueFromPointer() throws NativeException {
    wYear = getNextShort();
    wMonth = getNextShort();
    wDayOfWeek = getNextShort();
    wDay = getNextShort();
    wHour = getNextShort();
    wMinute = getNextShort();
    wSecond = getNextShort();
    wMilliseconds = getNextShort();
    return this;
  }

  public SystemTime() throws NativeException {
    super(null);
    createPointer();
  }

  public String toString() {
    return wYear + "/" + wMonth + "/" + wDay + " at + " + wHour + ":" + wMinute + ":" + wSecond + ":" + wMilliseconds;
  }

  public static SystemTime GetSystemTime() throws NativeException, IllegalAccessException {
    // 创建对象
    JNative nGetSystemTime = new JNative("Kernel32.dll", "GetSystemTime");
    SystemTime systemTime = new SystemTime();
    // 设置参数
    nGetSystemTime.setParameter(0, systemTime.getPointer());
    nGetSystemTime.invoke();
    // 解析结构指针内容
    return systemTime.getValueFromPointer();
  }

  public static void main(String[] args) throws NativeException, IllegalAccessException {
    System.err.println(GetSystemTime());
  }
}
這是呼叫 kernel32.dll 裡的 GetSystemTime,這個 dll 檔是在 Windows/system32 裡。

耶?執行失敗!
Exception in thread "main" java.lang.IllegalStateException: JNative library not loaded, sorry !
 at org.xvolks.jnative.JNative.(JNative.java:502)
 at org.xvolks.jnative.JNative.(JNative.java:427)
 at idv.neil.cttn.ComBean.main(ComBean.java:47)
Google 了半天,發現 dll 有分 32 位元和64 位元,這個 JNative 應該是 32 位元的,傷腦筋,我從 OS、JDK 到 Eclipse 都是 64 位元的,JDK 和 Eclipse 要改還有可能,若要改 OS 那不如換別個 library 算了。

後來相當幸運的,只將 Eclipse 用的 JDK 改成 32 位元就執行成功了,OS 和 Eclipse 都還是 64 位元的。

再來試試自己的 dll 檔。
public class MyDLL {

  public static void main(String[] args) throws NativeException, IllegalAccessException {
    JNative jn = new JNative("HS380A.dll", "OpenPort");
    jn.invoke();
    System.out.println(jn.getRetVal());
  }

}
因為沒有參數傳遞,就先不用 JNative 的 AbstractBasicData,一執行又出錯了。
Exception in thread "main" org.xvolks.jnative.exceptions.NativeException: DLL HS380A.dll not found
 at org.xvolks.jnative.JNative.nLoadLibrary(Native Method)
 at org.xvolks.jnative.JNative.loadLibrary(JNative.java:534)
 at org.xvolks.jnative.JNative.<init>(JNative.java:510)
 at org.xvolks.jnative.JNative.<init>(JNative.java:427)
 at idv.neil.cttn.MyDLL.main(MyDLL.java:16)
找不到 DLL 檔?依樣畫葫蘆,將我的 dll  丟到 windows/system32 裡,噹,居然不行!

只好再去請教 Google,試過 LD_LIBRART_PATH 和 -Djava.library.path 都無效,去看 JNative 的 Source code 發現:
private native int nLoadLibrary(String dllName, String funcPointer) throws NativeException;
喵的咧,居然將載入 dll 段寫在 dll裡,好樣的。

最後發現,只要將 dll 放在 Eclipse project 裡就可以了!
---

沒有留言:

張貼留言