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



JDKのダウンロードと環境変数の設定

Oracleのサイトから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プロジェクトの設定

プロジェクトのプロパティで 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のクラスファイルの準備

Javaのソースコードを準備する. 例えば,以下のような内容をテキストエディタに書き,Test.javaとして保存する.

public class Test {
    public static String run()
    {
        return "Hello world!";
    }
}

そして,コマンドプロンプトから,

javac Test.java

としてコンパイルする.何もメッセージが出なければ成功している. 成功したらTest.classというファイルができていることを確認しておく.

C/C++のコードからJavaのクラスを参照する

JNIを使うので以下のようにヘッダをインクルードする.

#include <jni.h>

Javaのコードを実行するために,まずJavaVMを起動する.

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)として,クラスファイルがある場所を実行ディレクトリ"."にしてある.

次にクラスを検索して,インスタンス化する.

// クラス検索
	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を取得する

// 各メソッドをインスタンスから呼び出す
	//  第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がそろったらいよいよ関数を呼び出す.

jstring jstr = (jstring)(env->CallObjectMethod(obj, mid));

返値がない関数だったらCallVoidMethod,int型を返す関数ならCallIntMethodなどそれぞれ異なるので注意. 今回の場合,文字列を返す関数なので,jstring型として文字列を受け取っている. このままではC/C++コード側で使えないので,char型に変換する.

// jstring -> char*の変換
	const char* cstr = env->GetStringUTFChars(jstr, 0);
	char *str = strdup(cstr);
	env->ReleaseStringUTFChars(jstr, cstr);

	cout << str << endl;

最後にJavaVMを破棄して終了.

// JavaVMの破棄
	res = jvm->DestroyJavaVM();
	if(res){
		cout << "could not destroy JavaVM : " << res << endl;
		return 1;
	}

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2024-03-08 (金) 18:06:02