torutkのブログ

ソフトウェア・エンジニアのブログ

JNIでJavaVMを起動

C/C++アプリケーションからJavaVMを起動し、Javaのクラスを呼び出す実験をしてみました。実に簡単にできるものと感心しました。

JavaVMの起動オプション指定

コマンドラインからJavaを実行する際に指定する書式とほぼ一緒です。JavaVMOption構造体の配列を定義し、それぞれの構造体のoptionStringフィールドにオプション文字列を設定します。
このJavaVMOption構造体の配列は、JavaVMInitArgs構造体のoptionsフィールドにセットします。

JavaVMOption options[3];
options[0].optionString = "-Xmx128m";
options[0].optionString = "-verbose:gc";
options[0].optionString = "-Djava.class.path=C:/home/torutk/myjava/jni";

JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 3;

JavaVMOption構造体の定義(jni.h)

typedef struct JavaVMOption {
    char* optionString;
    void* extraInfo;
} JavaVMOption;

JavaVMInitArgs構造体の定義(jni.h)

typedef struct JavaVMInitArgs {
    jint version;
    jint nOptions;
    JavaVMOption* options;
    jboolean ignoreUnrecognized;
} JavaVMInitArgs;

JNI_VERSION_1_Xの定義(jni.h)

#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004

JavaVMの起動

JNIEnv* env;
JavaVM* jvm;

int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (ret < 0) {
    // エラー処理
}

JNI_CreateJavaVM関数の戻り値は、

#define JNI_OK        0      /* success */
#define JNI_ERR       (-1)   /* unknown error */
#define JNI_EDETACHED (-2)   /* thread detached from the VM */
#define JNI_EVERSION  (-3)   /* JNI version error */
#define JNI_ENOMEM    (-4)   /* not enough memory */
#define JNI_EEXIST    (-5)   /* VM already created */ 
#define JNI_EINVAL    (-6)   /* invalid arguments */

コンパイルと実行

JDKに含まれるJNIのヘッダーファイル2つ、jni.hとjni_md.hを使用するので、インクルードパスにこの両者のあるディレクトリを指定します。

VC++ Toolkit 2003の場合

コンパイル時に、jni.hが入っている%JAVA_HOME%\include、jni_md.hが入っている%JAVA_HOME%\include\win32へのインクルードパスを指定します。
リンカには、インポートライブラリjvm.libとそのパス%JAVA_HOME%\libを指定します。

C:\home\torutk\myjava\jni> cl /IC:\jdk1.5.0\include /IC:\jdk1.5.0\include\win32 
/EHsc launch.cpp /link /LIBPATH:C:\jdk1.5.0\lib jvm.lib

実行時には、jvm.dllが必要となるので、環境変数PATHにjvm.dllがあるディレクトリを追加します。jvm.dllは、client用VMとserver用VMと2つ存在します。client用VMの場合、%JAVA_HOME%\jre\bin\clientを指定します。

C:\home\torutk\myjava\jni>PATH=%PATH%;C:\jdk1.5.0\jre\bin\client
C:\home\torutk\myjava\jni>launch

JDKのsrc.zipに含まれるlauncher/jvm_md.hには、レジストリを検索してJREのパスを選択する関数GetPublicJREHomeの実装があります。

  1. レジストリの\\HKEY_LOCAL_MACHINE\SOFTWARE|JavaSoft\Java Runtime Environmentを開く
  2. このキーのCurrentVersionの値を取り出す
  3. このキーのサブキーを2.で取得した値として開く
  4. 3.で開いたキーからJavaHomeの値を取り出す