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; } }}