全部まとめてサンプルコード

>JNIの使い方

ここまでをまとめてサンプルコード


ここまでサンプルコードを作ったのでVCのプロジェクトを公開する。お役にたてば幸い。
https://github.com/max-waltham/JNI_SAMPLE

分かればよいのでいろいろ省略する。
まず最初に、jvm.dllをLoadLibraryしてきちんとJVMを生成し、スレッドからのアクセスで正しいJNIEnvを返すコード。
#include <AtlSecurity.h>
#include <tchar.h>
#include <vector>
#include "jni.h"
 
#ifndef tstring
typedef std::basic_string<_TCHARtstring;
#endif
 
CRITICAL_SECTION gSection;
HINSTANCE jvmDllModule(NULL);
static JavaVMjvm_1(NULL);
 
void CreateSingleJavaVM()
{
   if (jvm_1 != NULL) {
      return;
   }
 
   tstring jvmDll = _T("C:\\java\\jre\\bin\\client\\jvm.dll");
   jvmDllModule = LoadLibrary(jvmDll.c_str());
   if (jvmDllModule == NULL) {
      throw -1;
   }
 
   int (__stdcall * createJavaVM)(JavaVM **, void **, void *);
   createJavaVM = (int (__stdcall *)(JavaVM **, void **, void *))
                  GetProcAddress(jvmDllModule, "JNI_CreateJavaVM");
 
   tstring searchPath;
   searchPath += _T("C:\\MyLibDir\\*.jar");
   WIN32_FIND_DATA fd;
   HANDLE hFind = FindFirstFile(searchPath.c_str(), &fd);
   
   tstring classpathStr = _T("-Djava.class.path=.;");
   if (hFind != INVALID_HANDLE_VALUE) {
      do {
         classpathStr += _T("C:\\MyLibDir\\");
         classpathStr += fd.cFileName;
         classpathStr += _T(";");
      } while (FindNextFile(hFind, &fd));
   }
   FindClose(hFind);
 
   USES_CONVERSION_EX;
   char* classpathOptStr = T2A_EX_DEF(classpathStr.c_str());
 
   JavaVMInitArgs vm_args;
   std::vector<JavaVMOption> opts;
   JavaVMOption clsPathOpt;
   clsPathOpt.optionString = classpathOptStr;
   opts.push_back(clsPathOpt);
 
   JavaVMOption* options = new JavaVMOption[opts.size()];
   for (size_t i = 0, l = opts.size(); i < l; i++) {
      options[i] = opts.at(i);
   }
   vm_args.options = options;
   vm_args.nOptions = (jint)opts.size();
   vm_args.version = JNI_VERSION_1_6;
   vm_args.ignoreUnrecognized = JNI_TRUE;
 
   JNIEnv* env = NULL;
   int status;
   try {
      status = createJavaVM(&jvm_1, (void**)&env, &vm_args);
   } catch (...) {
      throw -2;
   }
   delete [] options;
   options = NULL;
 
   switch (status) {
   case JNI_OK:
      break;
   case JNI_ERR:
      throw -3;
   case JNI_ENOMEM:
      throw -4;
   }
}
 
void GetRightJNIEnv(void **penv)
{
   if (jvm_1 == NULL) {
      EnterCriticalSection(&gSection);
      CreateSingleJavaVM();
      LeaveCriticalSection(&gSection);
   }
 
   jint ret = jvm_1->GetEnv(penv, JNI_VERSION_1_6);
   if (ret != JNI_OK) {
      ret = jvm_1->AttachCurrentThread(penv, NULL);
      if (ret != JNI_OK) {
         throw -5;
      }
   }
}
ちょー長くなった。しかしこのくらい作り込むと利用段階ではGetRightJNIEnvだけ使えば良いし、ライブラリを追加したいときもディレクトリに放り込むだけで良くなる(C:\Java\jreがJREのフォルダ、C:\MyLibDirがjarファイルを置くフォルダという状況)。
GetRightJNIEnvでスレッド内でアタッチしたJNIEnvへのポインタをキャッシュしておいて同じスレッドで2回目に呼び出されたときはキャッシュから返すようにすると効率がいい。ただし同じスレッドで2ヶ所でenvを使う場合はDettachのタイミングが難しいので良く考えなければならない。まぁ最悪プロセス終了までほっといても…


(以前のサンプルコードも一応残しておく。マルチスレッドでJavaを処理する場合とか、よく使うjclassを一箇所で管理するとか
//グローバル変数
JavaVM* jvm;
jclass cls;
jmethodID constractor;
jmethodID method;
/*
 * この関数がそのままスレッドとして動くらしい
 */
threadFunc(int threadID)
{
   // スレッド内で使うenv
   JNIEnv* env; 
   // envアタッチ
   jvm->AttachCurrentThread((void**)&env,NULL);
   
   // いろいろ処理
   jobject obj = env->NewObject(cls,constractor);
   env->CallObjectMethod(obj, method); // メソッドを使う
   env->DeleteLocalRef(obj); // delete
   // envデタッチ
   jvm->DetachCurrentThread();
}
 
_tmain()
{
   JNIEnv* env;
   JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); 
 
   cls = env->FindClass(...); // (スレッドで使う)クラスを見つける
   env->NewGlobalRef(cls); // 別スレッドから使えるようにする
   
   constractor = env->GetMethodID(...); // メソッドIDを見つける
   method = env->GetMethodID(...); // メソッドIDを見つける
 
   // Thread関係
   HANDLE hThread[2];
   DWORD dwThreadID[2];
   hThread[0] = CreateThread(NULL, 0,
                        (LPTHREAD_START_ROUTINEthreadFunc,
                        (LPVOID)0, 0, &dwThreadID[0]);
   hThread[1] = CreateThread(NULL, 0,
                        (LPTHREAD_START_ROUTINEthreadFunc,
                        (LPVOID)1, 0, &dwThreadID[1]);
   WaitForMultipleObjects(2, hThread, TRUEINFINITE);
   CloseHandle(hThread[0]);
   CloseHandle(hThread[1]);

   env->DeleteGlobalRef(cls); // 
   // jvm->DestoryJavaVM();はしない
}


0 件のコメント:

コメントを投稿