ここまでをまとめてサンプルコード
ここまでサンプルコードを作ったので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<_TCHAR> tstring; #endif CRITICAL_SECTION gSection; HINSTANCE jvmDllModule(NULL); static JavaVM* jvm_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_ROUTINE) threadFunc, (LPVOID)0, 0, &dwThreadID[0]); hThread[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) threadFunc, (LPVOID)1, 0, &dwThreadID[1]); WaitForMultipleObjects(2, hThread, TRUE, INFINITE); CloseHandle(hThread[0]); CloseHandle(hThread[1]); env->DeleteGlobalRef(cls); // // jvm->DestoryJavaVM();はしない }
0 件のコメント:
コメントを投稿