JavaからC言語の関数を呼び出そう
ふぃぃ〜.やっと卒業研究終わったぞー!!イエス!!
class JniDemo
{
public static void main ( String[] args )
{
JniNative jn = new JniNative ();
//普通にメソッドを呼び出す
jn.nativeMethod ();
}
}
class JniNative
{
// このメソッドの本体はC言語で記述します.
public native void nativeMethod ();
// これでプログラム開始時に NativeDll.dll をリンクします.
static
{
System.loadLibrary ( "NativeDll" );
}
}
> javac JniNative.java次に,この.classファイルから,DLL用のヘッダファイルを作成します.このとき使用するツールが javah です.また,指定するファイルは .javaファイルではないので .javaは付けちゃダメです.
> javah JniNativejavahを実行すると,次のようなC言語用のヘッダファイル(JniNative.h)が作成されます.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JniNative */
#ifndef _Included_JniNative
#define _Included_JniNative
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JnikNative
* Method: displayMessage
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniNative_nativeMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
後は,このヘッダファイルに基づいて DLL を作成すればOKです.今回はVC6.0で作りました.さて,このときメソッドに変化がおきているのに気をつけましょう.JavaとC言語間で変数やオブジェクトの扱いが異なるので,微妙に変わってます.まず,Javaクラスのフィールド(メンバ変数)等にアクセスできるように,引数の無かったメソッドでも引数が2つ増やされています.次に,メソッドの名前も変わってます.変更後の命名規則はJava_クラス名_メソッド名となります.(…と書かれてますがこの限りでは無いです.)
#include <jni.h>
#include "JniNative.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_JniNative_nativeMethod
(JNIEnv *env, jobject obj)
{
printf( "JNIのテスト\n同情よりも暇をくれ\n" );
}
さて,Jniを使用する場合は jni.h をインクルードしておかなくてはならないのですが,このファイルや,jni.h内部で呼び出される他のヘッダファイルなどは,JDKをインストールしたディレクトリの中の include ディレクトリの中に一式入ってます.> javac *.java現在のカレントディレクトリが,CurrentDir という名前だとすると,実行に必要なファイル構成は次のようになっているハズです.
CurrentDir +- JniDemo.class +- JniNative.class +- NativeDll.dllいよいよ実行!!
> java JniDemo JNIのテスト 同情よりも暇をくれむぅーん.以外と簡単だったね.すごいねSUN!(●^o^●)
パッケージ化してJNIを使う
とまぁ,以外と簡単だったJNIだったんだけど,これがパッケージ化するとうまくいきやがらねぇ!!(早くもキレ気味.Ca不足か?)
CurrentDir
+- JniDemo.class
+- PackageDir
+- JniNative.class
+- NativeDll.dll
といった環境で使いたかったわけさ.dllやそれを呼び出すクラスなんかは自分で設計してパッケージ化(上の例ではPackageDir)してしまい,そのクラス(あるいはパッケージ)を他の人が使用してアプリケーション(上の例ではJniDemo.class)とかを作るっつーのを夢見ていたワケさ.
import PackageDir.JniNative ;
class JniDemo
{
public static void main ( String[] args )
{
JniNative jn = new JniNative ();
//普通にメソッドを呼び出す
jn.nativeMethod ();
}
}
JniNative.java
package PackageDir ;
class JniNative
{
// このメソッドの本体はC言語で記述します.
public native void nativeMethod ();
// これでプログラム開始時に NativeDll.dll をリンクします.
static
{
System.loadLibrary ( "NativeDll" );
}
}
そして,コンパイルして,次のディレクトリ構成にして実行します.
CurrentDir
+- JniDemo.class
+- PackageDir
+- JniNative.class
+- NativeDll.dll
> java JniDemo Exception in thread "main" java.lang.UnsatisfiedLinkError: no NativeDll in java.library.pathぐっ… やっぱりダメだったにゃー.WindowsだとDLLをプログラムを起動するディレクトリか,SYSTEMディレクトリに置かないといけないからにゃー.
CurrentDir
+- JniDemo.class
+- NativeDll.dll ←ここに置くしかないかにゃー
+- PackageDir
+- JniNative.class
よーし,今度こそッ!!
> java JniDemo Exception in thread "main" java.lang.UnsatisfiedLinkError: nativeMethodなんでですのん?(TдT)
Java_クラス名_メソッド名って書いてあるっていいましたが,あれはパッケージ化してない場合の命名規則らしく,パッケージ化した場合は次のようになります.
Java_パッケージ名_クラス名_メソッド名がびーん.Σ( ̄ロ ̄;)
JNIEXPORT void JNICALL Java_PackageDir_JniNative_nativeMethodさて,名前の変更後,もう一度 NativeDll.dll を作り直して,実行します.
CurrentDir
+- JniDemo.class
+- NativeDll.dll
+- PackageDir
+- JniNative.class
> java JniDemo DLLのテスト 同情よりも暇をくれやったにゃー!! 成功!!