!!!JNI Javaにおいて、C/C++のモジュールを呼び出すことができる、または、逆にC/C++からJavaの機能を呼び出すことができるインターフェースを「JNI」(Java Native Interface)と言います。 JNIを使用することにより、JavaではタッチできなかったOSネイティブな処理や速度の必要な処理をC/C++言語で行ったりできるようになります。 以下に、その手順を説明します。 ここでは、JavaからC言語の関数を呼び出す方法を記述していきます。 !!Javaソースを記述 呼び出す側のJavaのソース「JNITest.java」を以下のようにします。 [ JNITest.java ] public class JNITest { static { System.loadLibrary("JNITest"); } public native String getText(); public native int add(int a,int b); public static void main(String[] args) { JNITest jni = new JNITest(); String str; //C言語に関連付けた関数「getText()」を呼び出し str = jni.getText(); System.out.println(str); //C言語に関連付けた関数「add()」を呼び出し int a,b; a=51; b=23; System.out.println("加算 : "+ a + "+" + b + "=" + jni.add(a,b)); } } この場合は、(Windowsの場合)「JNITest.dll」をライブラリとして読み込みます。 これの作成については後述します。 Linuxの場合は、拡張子が「so」になるでしょうか。 そして、「'''native'''」指定で関数「String getText()」と「int add(int a,int b)」を指定しています。 この「native」が記述された関数が、JavaからCを呼び出す関数の宣言となります。 ここではC言語側から、「getText()」で"HelloWorld!!"の文字列を返し、「add(int a,int b)」でa+bの計算結果を返すものとします。 「JNITest jni = new JNITest();」でネイティブの関数呼び出しのオブジェクトを生成して、各関数を呼び出しています。 !!C言語用ヘッダの出力 そして、C言語部分のモジュールを作成するための準備をします。 まずは、以下のように普通にJavaソースをコンパイルします。 javac JNITest.java 次に、C言語で使用するヘッダファイルを生成します。 javah -jni -o JNITest.h JNITest 「-o JNITest.h」は出力するC言語のヘッダ名を指定していますが、省略もできます。省略した場合は、その後の「クラス名.h」が出力されることになります。 最後の「JNITest」が、「JNITest.java」をコンパイルした後に生成されるクラス名を指定しています。 これで、以下のような「JNITest.h」が自動生成されます。 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class JNITest */ #ifndef _Included_JNITest #define _Included_JNITest #ifdef __cplusplus extern "C" { #endif /* * Class: JNITest * Method: getText * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_JNITest_getText (JNIEnv *, jobject); /* * Class: JNITest * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_JNITest_add (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif !!C言語側の準備 次は、C言語側の準備をします。 C言語で提供するモジュールは、「共有ライブラリ(DLL)」である必要があります。 VC++/BCCなどで、DLLを作成する設定を行ってください。 そして、 インクルードファイル検索パスに「Javaのインストールディレクトリ\include」と(Windowsの場合は)「Javaのインストールディレクトリ\win32」を加えます。 ライブラリファイルの検索パスに「Javaのインストールディレクトリ\lib」を加え、「jvm.lib」をリンク時の構成に加えてください。 そして、先ほど自動生成した「JNITest.h」を、コンパイラが検索できるインクルードのパス位置にコピーしてきてください。 !!C言語側のソースの記述 C言語側のソースは、以下の感じになります。 [JNITest.cpp] #include #include #include #include #include "JNITest.h" //javahで生成したヘッダを指定 BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } JNIEXPORT jstring JNICALL Java_JNITest_getText(JNIEnv *env, jobject o) { jstring str = env->NewStringUTF("HelloWorld!!"); return str; } JNIEXPORT jint JNICALL Java_JNITest_add(JNIEnv *env, jobject o, jint a, jint b) { jint sum; sum = a + b; return sum; } このときの関数名「Java_JNITest_getText」「Java_JNITest_add」や引数などの記述は、ヘッダ「JNITest.h」よりそのままコピーしてきています。 「Java_JNITest_getText」は、"HelloWorld!!"の文字列を返し、 「Java_JNITest_add」は、「引数の(a+b)」を結果として返しています。 ちなみに、Windows以外のOSでは、「DllMain」の関数記述はいらないです。 これをコンパイルすると、無事「JNITest.dll」が生成されます。 !!JNIを使ったJavaプログラムの実行 生成した「JNITest.dll」を、コマンドラインでJavaを実行するときに参照できる位置(PATHの通った位置)、もしくはカレントディレクトリにコピーします。 そして、一番始めに生成したJavaの「JNITest」を実行します。 javac JNITest 如何でしょうか? HelloWorld!! 加算 : 51+23=74 と表示されましたか? 簡単な処理をJavaからC言語呼び出しで行ったのですが、もっと時間のかかるような処理をC言語側で実行することで、 Javaのネックとなる「速度」の問題を緩和することができるようになるかと思います。 総合開発環境の「Eclipse」もJNI経由で「[[SWT]]」を呼び出しているようですね。 なので、動作速度がSwingに比べて快適です。 !!日本語文字列を返す 以下はおまけですが、日本語文字列(SHIFT-JIS)を返す場合は、その変換処理をC言語側で行ってあげる必要があります。 JavaはUTF-8で文字列を扱いますので、「WindowsのC言語→Java」へは、「SHIFT-JIS→UTF-8」の変換処理が必要です。 変換方法は、以下が一番楽です。 例えば、先ほどの「getText」で「はろーわーるど」と返す場合は、以下でOKです。 JNIEXPORT jstring JNICALL Java_JNITest_getText(JNIEnv *env, jobject o) { const char *pBuff = "はろーわーるど"; int len = strlen(pBuff); //Unicode文字列の長さを取得 int uLen = MultiByteToWideChar( CP_ACP, 0, pBuff, len , NULL , 0); WCHAR *retBuff = new WCHAR[uLen]; //Unicode文字列に変換する MultiByteToWideChar( CP_ACP, 0, pBuff, len , retBuff , uLen); //Javaの文字列生成 jstring ret = env->NewString((jchar *)retBuff , uLen); delete retBuff; return ret; } ただ、OS依存の「MultiByteToWideChar」はWindows以外では使えませんので、各OS対応の変換処理を行うか、Javaの「java.lang.String」をマッピングして変換してやるといいかもしれません。 #昔、仕事で「java.lang.String」を呼び出して対応した覚えがあるのですが、ソースが見つからない(^_^;;