Visual Studio 2010でJNIを使ってC/C++のコードからJavaのコードを扱う方法
-----
#contents
-----

*JDKのダウンロードと環境変数の設定 [#h0d2715e]
[[Oracleのサイト:http://www.oracle.com/technetwork/java/javase/downloads/index.html]]からJDKをダウンロードしてインストールする.
OSがx64でもアプリケーションがWin32ならばx86版をインストールすること.

環境変数PATHに
 C:\Program Files (x86)\Java\jdk1.7.0_45\jre\bin\client
を追加する.jdk1.7.0_45の部分は適時バージョンに合わせて書き換えること.
また,64ビットアプリケーションの場合は,Program Files (x86)の部分をProgram Filesとする.さらにサーバの場合は最後をserverにする.

Windows 7なら
 システムのプロパティ → システムの詳細設定 → 詳細設定タブの環境設定ボタン
で環境変数を設定できる.
システム環境変数の変数Pathに上記を追加する.ついでに
 C:\Program Files (x86)\Java\jdk1.7.0_45\bin
も追加しておくと便利(下記javapなどを使う場合に必要).


*Visual Studioプロジェクトの設定 [#o4152632]
プロジェクトのプロパティで C/C++ → 全般 → 追加のインクルードディレクトリ に
 C:\Program Files (x86)\Java\jdk1.7.0_45\include
 C:\Program Files (x86)\Java\jdk1.7.0_45\include\win32
を追加.
リンカ → 全般 → 追加のライブラリディレクトリに
 C:\Program Files (x86)\Java\jdk1.7.0_45\lib
を追加する.
いずれもJDKのバージョンや環境に合わせて適切に書き換えること.
リンク時には jvm.lib が必要なので,リンカ → 入力 → 追加の依存ファイル に追加しておくか,コード中に
 #pragma comment(lib, "jvm.lib")
と記述しておく.


*Javaのクラスファイルの準備 [#t680cc90]
Javaのソースコードを準備する.
例えば,以下のような内容をテキストエディタに書き,Test.javaとして保存する.
#code(java){{
public class Test {
    public static String run()
    {
        return "Hello world!";
    }
}
}}
そして,コマンドプロンプトから,
 javac Test.java
としてコンパイルする.何もメッセージが出なければ成功している.
成功したらTest.classというファイルができていることを確認しておく.


*C/C++のコードからJavaのクラスを参照する [#neba7f7c]
まず,JavaVMを起動する.
JNIを使うので以下のようにヘッダをインクルードする.
 #include <jni.h>

Javaのコードを実行するために,まずJavaVMを起動する.
#code(java){{
	JNIEnv *env;
	JavaVM *jvm;

	// JavaVM起動オプションの設定
	JavaVMOption options[1];
	options[0].optionString = "-Djava.class.path=.";

	JavaVMInitArgs vm_args;
	vm_args.version = JNI_VERSION_1_6;
	vm_args.options = options;
	vm_args.nOptions = 1;
	//vm_args.ignoreUnrecognized = true;

	// JavaVMを初期化,起動
	int res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
	if(res){
		cout << "cannot run JavaVM : " << res << endl;
		return 1;
	}
}}
起動オプション(options)として,クラスファイルがある場所を実行ディレクトリ"."にしてある.

次にクラスを検索して,インスタンス化する.
#code(java){{
	// クラス検索
	jclass cls = env->FindClass("Test");
	if(cls == 0){
		cout << "could not found class : Test" << endl;
		return 1;
	}

	// クラス情報をインスタンス化するために<init>メソッドのメソッドIDを取得
	jmethodID cns = env->GetMethodID(cls, "<init>", "()V");
	if(cns == NULL){
		cout << "could not get <init> method." << endl;
		return 1;
	}
	jobject obj = env->NewObject(cls, cns);
}}
インスタンス化したクラス(obj)を介してメソッド(メンバ関数)にアクセスする.

各メソッドを呼び出すためにはメソッドIDを取得する

#code(java){{
	// 各メソッドをインスタンスから呼び出す
	//  第3引数のシグネチャは javap -s Test などで調べられる(引数と返値を表す)
	jmethodID mid = env->GetStaticMethodID(cls, "run", "()Ljava/lang/String;");
	if(mid == NULL){
		cout << "could not get method : " << "run" << endl;
		return 1;
	}
}}
GetStaticMethodIDの第2引数は関数名,
第3引数は関数の引数と返値(シグネチャ)を指定している.
シグネチャは,
 javap -s Test
とすると調べられる.

インスタンスとメソッドIDがそろったらいよいよ関数を呼び出す.
#code(java){{
	jstring jstr = (jstring)(env->CallObjectMethod(obj, mid));
}}
返値がない関数だったらCallVoidMethod,int型を返す関数ならCallIntMethodなどそれぞれ異なるので注意.
今回の場合,文字列を返す関数なので,jstring型として文字列を受け取っている.
このままではC/C++コード側で使えないので,char型に変換する.
#code(java){{
	// jstring -> char*の変換
	const char* cstr = env->GetStringUTFChars(jstr, 0);
	char *str = strdup(cstr);
	env->ReleaseStringUTFChars(jstr, cstr);

	cout << str << endl;
}}

最後にJavaVMを破棄して終了.
#code(java){{
	// JavaVMの破棄
	res = jvm->DestroyJavaVM();
	if(res){
		cout << "could not destroy JavaVM : " << res << endl;
		return 1;
	}
}}

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS