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として保存する.

  1
  2
  3
  4
  5
  6
public class Test {
    public static String run()
    {
        return "Hello world!";
    }
}

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

javac Test.java

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

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

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

#include <jni.h>

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
    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)として,クラスファイルがある場所を実行ディレクトリ"."にしてある.

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
    // クラス検索
    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を取得する

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

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

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

  1
  2
  3
  4
  5
  6
    // jstring -> char*の変換
    const char* cstr = env->GetStringUTFChars(jstr, 0);
    char *str = strdup(cstr);
    env->ReleaseStringUTFChars(jstr, cstr);
 
    cout << str << endl;

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

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

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-01-09 (木) 13:14:53 (2651d)