2009年12月14日 星期一

[Java]Java呼叫C/C++ DLL

接續上一篇,這一陣子要寫可以動態連結的程式,其實我最先開始想到的就是用windows的DLL(Dynamic-link library )或是Linux的SO(Shared Object)

一來這樣速度快二來這樣子也有個更新的彈性,只是這樣一來可能就無法實現跨平台的優點。
在Java有提供JNI來實驗呼叫c\c++的dll功能

放幾個我參考的網站

一般來說會先寫個.java檔去產生Header。假設今天我一隻程式叫Hello.java
那我可以寫一個批次檔(.bat)去幫我產生要用的header

makeHeader.bat

javac Hello.java
javah -jni Hello
@pause

其中javah這個指令就是用來產生JNI Header,之後產生一個標頭檔叫Hello.h
寫一個.c或是.cpp去實作他產生DLL之後就可以給Java呼叫
不過老實說這樣很不方便,萬一Hello.java要增加新函式就會變得很不方便
因此我會比較建議用JNA+CDT的方式去做,如果要用Windows+Eclipse去開發C++的話,有兩個套件要先裝起來,一個是MinGW,另一個則是CDT



首先去下載MinGW
http://prdownloads.sourceforge.net/mingw/MinGW-5.0.3.exe
在安裝過程中選擇[Download and Install]去安裝,裝在預設目錄就行了
之後選項選擇[Candidate],要下載的東西則有
  • g++ compiler
  • g77 compiler
  • Objective C Compiler
  • MinGW Make

之後去設定環境變數
  • Path變數增加[MinGW安裝目錄\bin;] ex:C:\MinGW\bin;
  • 新增一個環境變數C_INCLUDE_PATH,值為:C:\MinGW\include
  • 新增CPLUS_INCLUDE_PATH,值為:C:\MinGW\include\c++ \xxx;C:\MinGW\include\c++\xxx\mingw32;C:\MinGW\include\c++\xxx\backward;C:\MinGW\include
    其中xxx的部分換成版本編號 ex:3.4.0

在來是安裝CDT的部分,先去官方網站找自己要的版本
http://www.eclipse.org/cdt/downloads.php

假設我要下載6.0x版,應該會看到一個連結
http://download.eclipse.org/tools/cdt/releases/galileo

這個就是eclipse的更新網址,更新的方法點選上方的[Help]->[Install New Software]
之後把那個網址Add進去之後就可以開始更新,更新完之後就可以[New]->[Project]產生一個c/c++的project

再來介紹一下JNA這玩意
官方網站:https://jna.dev.java.net/
參考網站:http://blog.csdn.net/shendl/archive/2008/12/23/3589676.aspx
               http://enijmax.2y.idv.tw/linux/CLib_Jni.html
               http://www.52language.com/Article/16857.html

JNA是一套Lib,他有個最大的好處,就是我不必在用javah去產生header實作才能呼叫DLL
我可以去輕易使用現有的DLL。
不過我會使用這玩意最大的玩意,就是我嘗試半天就是沒辦法生出實作javah產生header的DLL
後來使用JNA我就可以專心寫程式不用再去管那一大堆指令了

首先從官方網站把jna.jar下載下來後,在專案上按滑鼠右鍵點選[properties]->[Java Build Path]->[Libraries],選擇[Add External JARs],把剛才下載下來的jna.jar加進去後就可以使用了

那麼首先試試參考網站上的一個例子
考慮下方程式碼

import com.sun.jna.Native;
import com.sun.jna.Platform;
public class Test {

public interface CLibrary extends Library {

CLibrary INSTANCE = (CLibrary)

Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),

CLibrary.class);



void printf(String format, Object... args);

}

public static void main(String[] args) {

CLibrary.INSTANCE.printf("Hello %d", 123);
}

}
//output:Hello 123


在這裡他呼叫的printf是msvcrt裡的printf,也就是c語言的printf函式。連native關鍵字都被我拿掉了,真是神奇。

再來試試自己寫的dll,畢竟這才是最重要的部分阿

首先我產生一個C的Project 裡面兩個檔案Hello.h跟Hello.c

Hello.h內容

__declspec(dllimport) void hello ();


Hello.c內容

#include "Hello2.h"

void hello(void)
{ printf("\nHello My Baby!! \n");}


之後用cmd去下指令編譯出兩個檔案,不過我都是寫成.bat檔

create.bat

gcc -c Hello.c
gcc -shared -o Hello.dll Hello.o -Wl,——out-implib,libmessage.a

之後就會產生DLL檔,把他放到java專案底下,這裡要注意一下,在eclipse的話是放到src目錄之前一個目錄
否則就是放到.class同個目錄下

接下來測試程式

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class Test {
public interface MyPrint extends Library {

MyPrint INSTANCE = (MyPrint)

Native.loadLibrary("Hello",MyPrint.class);

public void hello();

}
public static void main(String[] args) {

MyPrint.INSTANCE.hello();

}
}
//output:Hello My Baby!!



這樣就大功告成了,不過在此要注意一點的就是 Native.loadLibrary裡面不要放副檔名
他自己會去判斷是DLL或是SO檔

沒有留言: