ルート情報を通知するJNIクラスを一部修正しました。
どうもsave_class()関数のロジックでjfieldID等を取得する部分を毎回実行するって言うのが気に入らなくて色々と試して見ました。
まぁ、ロジックが冗長になったのは、Javaから呼び出した時に内部エラーが発生していたので、てっきりC++のロジックの作り方の関係だろうと思って、落ち着いたのが先週までのロジックだったわけですが、その後試していて次のような流れでも問題なく動作することが分かったので公開します。
RouteCheck.cpp
#include "jniroutecheck.h" #include "jninet.h" static listener_list listener; static JavaVM *jvm = NULL; jclass ric_class; /* RouterInfo */ jmethodID ric_ctor; /* RouterInfo() */ jfieldID ric_indexID; /* RouterInfo.index */ jfieldID ric_destID; /* RouterInfo.dest */ jfieldID ric_maskID; /* RouterInfo.mask */ jfieldID ric_gatewayID; /* RouterInfo.gateway */ jfieldID ric_typeID; /* RouterInfo.type */ jfieldID ric_metricID; /* RouterInfo.metric */ jfieldID ric_defaultGwID; /* RouterInfo.defaultGw */ jfieldID ric_primaryGwID; /* RouterInfo.primaryGw */ jobject save_class(JNIEnv *env, jclass ric_class, RouteInfo info) { jobject netifObj = env->NewObject(ric_class, ric_ctor); env->SetIntField(netifObj, ric_indexID, info.getIndex()); env->SetObjectField(netifObj, ric_destID, cStringToJString(env, info.getDest())); env->SetObjectField(netifObj, ric_maskID, cStringToJString(env, info.getMask())); env->SetObjectField(netifObj, ric_gatewayID, cStringToJString(env, info.getGateway())); env->SetIntField(netifObj, ric_typeID, info.getType()); env->SetIntField(netifObj, ric_metricID, info.getMetric()); env->SetBooleanField(netifObj, ric_defaultGwID, info.isDefaultGateway()); env->SetBooleanField(netifObj, ric_primaryGwID, info.isPrimaryGateway()); return netifObj; } void WINAPI route_modified(route_list added_table, route_list removed_table) { jobjectArray add_routes = NULL; jobjectArray remove_routes = NULL; jint err; JNIEnv *base_env = NULL; JavaVMAttachArgs t_args = {JNI_VERSION_1_6, NULL, NULL}; if ((err = jvm->AttachCurrentThread((void**)&base_env, &t_args)) != JNI_OK) { return; } if (!added_table.empty()) { jsize rt_count = (jsize)added_table.size(); if (rt_count > 0) { add_routes = base_env->NewObjectArray(rt_count, ric_class, NULL); for (jsize i = 0; i < rt_count; i++) { RouteInfo info = added_table.at(i); jobject one_route = save_class(base_env, ric_class, info); base_env->SetObjectArrayElement(add_routes, i, one_route); } } } if (!removed_table.empty()) { jsize rt_count = (jsize)removed_table.size(); if (rt_count > 0) { remove_routes = base_env->NewObjectArray(rt_count, ric_class, NULL); for (jsize i = 0; i < rt_count; i++) { RouteInfo info = removed_table.at(i); jobject one_route = save_class(base_env, ric_class, info); base_env->SetObjectArrayElement(remove_routes, i, one_route); } } } for (ll_iter iter = listener.begin(); iter != listener.end(); iter++) { jobject rc_obj = (*iter)->caller; jclass rc_class = (jclass)base_env->GetObjectClass(rc_obj); jmethodID rc_callback = base_env->GetMethodID(rc_class, "routeModified", "([Lnet/RouteInfo;[Lnet/RouteInfo;)V"); base_env->CallVoidMethod(rc_obj, rc_callback, add_routes, remove_routes, NULL); } jvm->DetachCurrentThread(); } JNIEXPORT void JNICALL Java_net_RouteCheck_init(JNIEnv *env, jclass cls) { /* * RouteInfoクラスの情報を設定 */ ric_class = env->FindClass("net/RouteInfo"); ric_ctor = env->GetMethodID(ric_class, "<init>", "()V"); ric_indexID = env->GetFieldID(ric_class, "index", "I"); ric_destID = env->GetFieldID(ric_class, "dest", "Ljava/lang/String;"); ric_maskID = env->GetFieldID(ric_class, "mask", "Ljava/lang/String;"); ric_gatewayID = env->GetFieldID(ric_class, "gateway", "Ljava/lang/String;"); ric_typeID = env->GetFieldID(ric_class, "type", "I"); ric_metricID = env->GetFieldID(ric_class, "metric", "I"); ric_defaultGwID = env->GetFieldID(ric_class, "defaultGw", "Z"); ric_primaryGwID = env->GetFieldID(ric_class, "primaryGw", "Z"); env->GetJavaVM(&jvm); } JNIEXPORT void JNICALL Java_net_RouteCheck_addRouteChangeListener(JNIEnv *env, jclass cls, jobject target) { PLISTENER obj = new LISTENER; obj->base = target; obj->caller = env->NewGlobalRef(target); listener.push_back(obj); if (listener.size() == 1) { RouteCheck::addCallBackRoutine(route_modified); } return ; } JNIEXPORT void JNICALL Java_net_RouteCheck_removeRouteChangeListener(JNIEnv *env, jclass cls, jobject target) { jobject remove = env->NewGlobalRef(target); for (ll_iter iter = listener.begin(); iter != listener.end(); iter++) { PLISTENER obj = (*iter); if (env->IsSameObject(obj->caller, remove) == JNI_TRUE) { env->DeleteGlobalRef(obj->caller); if (listener.size() == 1) { RouteCheck::removeCallBackRoutine(route_modified); } listener.erase(iter); delete(obj); break; } } env->DeleteGlobalRef(remove); return ; } JNIEXPORT void JNICALL Java_net_RouteCheck_startLinstening(JNIEnv *env, jclass cls) { RouteCheck::startRouteModifyCheck(); } JNIEXPORT void JNICALL Java_net_RouteCheck_stopLinstening(JNIEnv *env, jclass cls) { RouteCheck::stopRouteModifyCheck(); }
始めの頃にJavaで実行した時のエラーだったロジックを詳しく覚えていないんですが、うろ覚えではJava_net_RouteCheck_init()の中でFindClassをした後に、NewGlobalRefでクラスをオブジェクトとして参照していたようで、
ric_class = env->FindClass("net/RouteInfo");
ric_class = (jclass)env->NewGlobalRef(ric_class);
これが後々の問題になったみたいです。
(実は以前のIfInfoクラス用のJNIのJava_net_IfInfo_initロジックの中でAddrInfoクラスを取得する時のもやってたんで、コピペしたのが悪かったかな・・・。
こっちも後で修正・テストしてみようっと。)
ルート情報の変更を通知するJavaのクラスを作りました。
C++用に作成されたクラスを利用してJavaでもルートの変更通知を受取るクラスを作りました。
Javaのクラスとしては、通知処理を司るRouteCheckクラスと、通知をイベントとして受取るRouteCheckListenerクラスを作りました。
RouteCheck.java
package net; public class RouteCheck { static { System.loadLibrary("jninet"); init(); } public RouteCheck() { } private static native void init(); public static native void addRouteChangeListener(RouteCheckListener listener); public static native void removeRouteChangeListener(RouteCheckListener listener); public static native void startLinstening(); public static native void stopLinstening(); }
RouteCheckListener.java
package net; public abstract class RouteCheckListener { public RouteCheckListener() { } abstract public void routeModified(RouteInfo[] added, RouteInfo[] removed); }
RouteCheckListenerクラスで通知を受取った時の処理として、もう一度この中からリスナーを呼び出すような方法でも良かったのですが、同じような処理が重複して出てくるよりも、abstractクラスとして作成し実際に利用する時はこのクラスを継承してもらう方法の方がスマートなような気がしたので、この様な作りになっています。
それで、いつものようにJNIを作成するために、RouteCheckクラスをコンパイルした後にjavahでヘッダファイルを作成し、そしてJNIの実態を作成しました。
jniroutecheck.h
#ifndef _JNI_ROUTECHECK_CLASS #define _JNI_ROUTECHECK_CLASS #include <windows.h> #include <vector> #include "routecheck.h" #include "net_RouteCheck.h" typedef struct _LISTENER { jobject base; jobject caller; } LISTENER, *PLISTENER; typedef std::vector<PLISTENER> listener_list; typedef listener_list::iterator ll_iter; #endif // _JNI_ROUTECHECK_CLASS
jniroutecheck.cpp
#include "jniroutecheck.h" #include "jninet.h" static listener_list listener; static JavaVM *jvm = NULL; jobject save_class(JNIEnv *env, jclass ric_class, RouteInfo info) { jmethodID ric_ctor = env->GetMethodID(ric_class, "<init>", "()V"); jobject netifObj = env->NewObject(ric_class, ric_ctor); jfieldID ric_indexID = env->GetFieldID(ric_class, "index", "I"); env->SetIntField(netifObj, ric_indexID, info.getIndex()); jfieldID ric_destID = env->GetFieldID(ric_class, "dest", "Ljava/lang/String;"); env->SetObjectField(netifObj, ric_destID, cStringToJString(env, info.getDest())); jfieldID ric_maskID = env->GetFieldID(ric_class, "mask", "Ljava/lang/String;"); env->SetObjectField(netifObj, ric_maskID, cStringToJString(env, info.getMask())); jfieldID ric_gatewayID = env->GetFieldID(ric_class, "gateway", "Ljava/lang/String;"); env->SetObjectField(netifObj, ric_gatewayID, cStringToJString(env, info.getGateway())); jfieldID ric_typeID = env->GetFieldID(ric_class, "type", "I"); env->SetIntField(netifObj, ric_typeID, info.getType()); jfieldID ric_metricID = env->GetFieldID(ric_class, "metric", "I"); env->SetIntField(netifObj, ric_metricID, info.getMetric()); jfieldID ric_defaultGwID = env->GetFieldID(ric_class, "defaultGw", "Z"); env->SetBooleanField(netifObj, ric_defaultGwID, info.isDefaultGateway()); jfieldID ric_primaryGwID = env->GetFieldID(ric_class, "primaryGw", "Z"); env->SetBooleanField(netifObj, ric_primaryGwID, info.isPrimaryGateway()); return netifObj; } void WINAPI route_modified(route_list added_table, route_list removed_table) { jobjectArray add_routes = NULL; jobjectArray remove_routes = NULL; jint err; JNIEnv *base_env = NULL; JavaVMAttachArgs t_args = {JNI_VERSION_1_6, NULL, NULL}; if ((err = jvm->AttachCurrentThread((void**)&base_env, &t_args)) != JNI_OK) { return; } jclass ric_class = base_env->FindClass("net/RouteInfo"); ric_class = (jclass)base_env->NewGlobalRef(ric_class); if (!added_table.empty()) { jsize rt_count = (jsize)added_table.size(); if (rt_count > 0) { add_routes = base_env->NewObjectArray(rt_count, ric_class, NULL); for (jsize i = 0; i < rt_count; i++) { RouteInfo info = added_table.at(i); jobject one_route = save_class(base_env, ric_class, info); base_env->SetObjectArrayElement(add_routes, i, one_route); } } } if (!removed_table.empty()) { jsize rt_count = (jsize)removed_table.size(); if (rt_count > 0) { remove_routes = base_env->NewObjectArray(rt_count, ric_class, NULL); for (jsize i = 0; i < rt_count; i++) { RouteInfo info = removed_table.at(i); jobject one_route = save_class(base_env, ric_class, info); base_env->SetObjectArrayElement(remove_routes, i, one_route); } } } for (ll_iter iter = listener.begin(); iter != listener.end(); iter++) { jobject rc_obj = (*iter)->caller; jclass rc_class = (jclass)base_env->GetObjectClass(rc_obj); jmethodID rc_callback = base_env->GetMethodID(rc_class, "routeModified", "([Lnet/RouteInfo;[Lnet/RouteInfo;)V"); base_env->CallVoidMethod(rc_obj, rc_callback, add_routes, remove_routes, NULL); } jvm->DetachCurrentThread(); } JNIEXPORT void JNICALL Java_net_RouteCheck_init(JNIEnv *env, jclass cls) { env->GetJavaVM(&jvm); } JNIEXPORT void JNICALL Java_net_RouteCheck_addRouteChangeListener(JNIEnv *env, jclass cls, jobject target) { PLISTENER obj = new LISTENER; obj->base = target; obj->caller = env->NewGlobalRef(target); listener.push_back(obj); if (listener.size() == 1) { RouteCheck::addCallBackRoutine(route_modified); } return ; } JNIEXPORT void JNICALL Java_net_RouteCheck_removeRouteChangeListener(JNIEnv *env, jclass cls, jobject target) { jobject remove = env->NewGlobalRef(target); for (ll_iter iter = listener.begin(); iter != listener.end(); iter++) { PLISTENER obj = (*iter); if (env->IsSameObject(obj->caller, remove) == JNI_TRUE) { env->DeleteGlobalRef(obj->caller); if (listener.size() == 1) { RouteCheck::removeCallBackRoutine(route_modified); } listener.erase(iter); delete(obj); break; } } env->DeleteGlobalRef(remove); return ; } JNIEXPORT void JNICALL Java_net_RouteCheck_startLinstening(JNIEnv *env, jclass cls) { RouteCheck::startRouteModifyCheck(); } JNIEXPORT void JNICALL Java_net_RouteCheck_stopLinstening(JNIEnv *env, jclass cls) { RouteCheck::stopRouteModifyCheck(); }
このロジック中のJava_net_RouteCheck_init処理の中でjvmを保存していますが、これはC++のRoureCheckクラスからroute_modified関数が呼ばれた時に、Javaの環境情報を渡せないためです。
テストでは、過去に使ったIfIntoTestクラスのロジックを以下のように一部修正し、
IfInfoTest.java
・・・・ RouteCheckTest test = new RouteCheckTest(); RouteCheck.addRouteChangeListener(test); RouteCheck.startLinstening(); System.out.println("Listening start"); RouteInfo entry = new RouteInfo(2, "202.239.113.0", "255.255.255.0", "172.16.255.1", 0, 10, false, false); RouteInfo.addRoute(entry); System.out.println("After add route"); ris = RouteInfo.getRouteList(); for (int i = 0; i < ris.length; i++) { printRoute(ris[i]); } RouteInfo.delRoute(entry); System.out.println("After delete route"); ris = RouteInfo.getRouteList(); for (int i = 0; i < ris.length; i++) { printRoute(ris[i]); } System.out.println("Listening stop"); RouteCheck.stopLinstening(); RouteCheck.removeRouteChangeListener(test); ・・・・
新たに通知イベントを受取るRouteCheckTestクラスを作成しました。
package net.test; import net.RouteCheckListener; import net.RouteInfo; public class RouteCheckTest extends RouteCheckListener { public RouteCheckTest() { } public void routeModified(RouteInfo[] added, RouteInfo[] removed) { if (added != null) { System.out.println("Routing information added."); for (int i = 0; i < added.length; i++) { print(added[i]); } } if (removed != null) { System.out.println("Routing information removed."); for (int i = 0; i < removed.length; i++) { print(removed[i]); } } } private void print(RouteInfo ri) { System.out.println("Route : "+ri.getDest()+"/"+ri.getMask()+" to "+ri.getGateway()+" "+ri.getMetric()+" "+ri.isDefaultGateway()); } }
ルート情報が変更されると通知するクラスを作りました
必要にせまられてルート情報の変更を取得したいということで、IPHLPAPIを使ってルート情報の変更を通知するクラスを作りました。
NotifyRouteChange()関数には、同期タイプと非同期タイプの両方あるのですが、今回は非同期モードにしてWaitForSingleObject()関数で通知をチェックする方法にしました。
routecheck.h
#ifndef _ROUTE_CHECK_CLASS #define _ROUTE_CHECK_CLASS #include <windows.h> #include <vector> #include "routeinfo.h" typedef void (WINAPI *lpRouteCallBackFunc)(route_list added_table, route_list removed_table); typedef std::vector<lpRouteCallBackFunc> callback_list; typedef callback_list::iterator cl_iter; class RouteCheck { private: static DWORD WINAPI NotifyThread(void *lpParameter); static void notify(route_list added_table, route_list removed_table); public: static void addCallBackRoutine(lpRouteCallBackFunc func); static void removeCallBackRoutine(lpRouteCallBackFunc func); static bool startRouteModifyCheck(); static bool stopRouteModifyCheck(); }; #endif // _ROUTE_CHECK_CLASS
routecheck.cpp
#include <winsock2.h> #include "routecheck.h" static bool loopctl = true; static bool active_check = false; static callback_list callback_routines; static HANDLE hThread; static OVERLAPPED overlap; /* * ルート情報が変更された時点でWindowsシステムから呼び出される * param : パラメータ(voidポインター) * * return : DWORD */ DWORD WINAPI RouteCheck::NotifyThread(void *lpParameter) { HANDLE hand = NULL; memset(&overlap, 0, sizeof(overlap)); overlap.hEvent = WSACreateEvent(); route_list org_route_list = RouteInfo::getRouteList(); loopctl = true; while (loopctl) { /* * ルート情報に変更があったか場合に通知する * return : NO_ERROR = 処理が成功した * その他 = エラー * 参照:http://msdn.microsoft.com/en-us/library/aa366332(VS.85).aspx */ unsigned long ret = NotifyRouteChange(&hand, &overlap); if (ret != NO_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { active_check = false; return 0; } } active_check = true; if (WaitForSingleObject(overlap.hEvent, INFINITE) == WAIT_OBJECT_0) { if (loopctl) { route_list new_route_list = RouteInfo::getRouteList(); route_list added_route_list = route_list(); for (size_t new_row = 0; new_row < new_route_list.size(); new_row++) { RouteInfo new_entry = new_route_list.at(new_row); bool found = false; for (route_list::iterator org_row = org_route_list.begin(); org_row != org_route_list.end(); org_row++) { RouteInfo org_entry = *org_row; if ((new_entry.getDest() == org_entry.getDest()) && (new_entry.getMask() == org_entry.getMask()) && (new_entry.getGateway() == org_entry.getGateway())) { found = true; org_route_list.erase(org_row); break; } } if (!found) { added_route_list.push_back(new_entry); } } notify(added_route_list, org_route_list); org_route_list.clear(); org_route_list = new_route_list; } } } org_route_list.clear(); active_check = false; return 0; } /* * ルート情報が変更された時点で登録されたプログラムを呼び出す * param : 追加されたルート情報(route_list) * 削除されたルート情報(route_list) * * return : 無し */ void RouteCheck::notify(route_list added_table, route_list removed_table) { for (size_t i = 0; i < callback_routines.size(); i++) { lpRouteCallBackFunc callbackfunc = callback_routines.at(i); callbackfunc(added_table, removed_table); } } /* * 呼び出すプログラムの登録をする * param : 登録するプログラムアドレス(RouteCallBackFuncポインター) * * return : 無し */ void RouteCheck::addCallBackRoutine(lpRouteCallBackFunc func) { callback_routines.push_back(func); } /* * 呼び出すプログラムの登録解除をする * param : 登録解除するプログラムアドレス(RouteCallBackFuncポインター) * * return : 無し */ void RouteCheck::removeCallBackRoutine(lpRouteCallBackFunc func) { for (cl_iter func_it = callback_routines.begin(); func_it != callback_routines.end(); func_it) { if (*func_it == func) { callback_routines.erase(func_it); break; } } } /* * ルート情報変更のチェックを開始する * param : 無し * * return : bool * true=開始が完了した * false=開始に失敗した */ bool RouteCheck::startRouteModifyCheck() { DWORD threadid = 0; hThread = CreateThread(NULL, 0, NotifyThread, NULL, 0, &threadid); if (hThread != NULL) { int loop = 100; while (!active_check && (loop > 0)) {Sleep(100); loop--;} if (loop > 0) return true; } return false; } /* * ルート情報変更のチェックを停止する * param : 無し * * return : bool * true=停止が完了した * false=停止に失敗した */ bool RouteCheck::stopRouteModifyCheck() { loopctl = false; SetEvent(overlap.hEvent); WSACloseEvent(overlap.hEvent); CloseHandle(hThread); int loop = 100; while (active_check && (loop > 0)) {Sleep(100); loop--;} if (loop > 0) return true; return false; }
テストプログラムは、以前作ったnetinfo.cppのgetRoute()関数にロジックを追加しました。
netinfo.cpp
void WINAPI callback_func1(route_list added_table, route_list removed_table) { printf("Callback routine execute 1\n"); for (size_t i = 0; i < added_table.size(); i++) { RouteInfo route = added_table.at(i); COUT << _T("Added route : ") << route.getDest() << _T(" ") << route.getMask() << _T(" ") << route.getGateway() << endl; } for (size_t i = 0; i < removed_table.size(); i++) { RouteInfo route = removed_table.at(i); COUT << _T("Removed route : ") << route.getDest() << _T(" ") << route.getMask() << _T(" ") << route.getGateway() << endl; } } void WINAPI callback_func2(route_list added_table, route_list removed_table) { COUT << _T("Callback routine execute 2") << endl; } void getRoute() { route_list list = RouteInfo::getRouteList(); for (size_t i = 0; i < list.size(); i++) { RouteInfo info = list.at(i); COUT << _T("RouteInfo ") << info.getIndex() << _T(" : ") << info.getDest() << _T("-") << info.getMask() << _T("-") << info.getGateway() << endl; } RouteInfo info = RouteInfo::getBestRoute(String(_T("202.239.113.18"))); COUT << _T("Gateway ") << info.getIndex() << _T(" : ") << info.getDest() << _T("-") << info.getMask() << _T("-") << info.getGateway() << endl; RouteCheck::addCallBackRoutine(callback_func1); RouteCheck::addCallBackRoutine(callback_func2); if (RouteCheck::startRouteModifyCheck()) { info = RouteInfo(0, String(_T("202.239.113.18")), String(_T("255.255.255.255")), String(_T("172.16.255.1")), 3, 10, false, false); RouteInfo::addRouteEntry(info); Sleep(500); COUT << _T("After add") << endl; list.clear(); list = RouteInfo::getRouteList(); for (size_t i = 0; i < list.size(); i++) { RouteInfo info = list.at(i); COUT << _T("RouteInfo ") << info.getIndex() << _T(" : ") << info.getDest() << _T("-") << info.getMask() << _T("-") << info.getGateway() << endl; } RouteInfo::deleteRouteEntry(info); Sleep(500); COUT << _T("After delete") << endl; list.clear(); list = RouteInfo::getRouteList(); for (size_t i = 0; i < list.size(); i++) { RouteInfo info = list.at(i); COUT << _T("RouteInfo ") << info.getIndex() << _T(" : ") << info.getDest() << _T("-") << info.getMask() << _T("-") << info.getGateway() << endl; } RouteCheck::stopRouteModifyCheck(); } }
Sleepが入っているのは、ルート情報の通知を受けたcallback_func1とcallback_func2での出力処理と、ルート情報一覧のロジックで正しく同期が取れていないので、表示がおかしくなるのを防ぐためです。
今日はこのクラスをJavaからも利用できるように実装できればいいかな。
コンソールを出さないように書き換えました。
やっぱり、Windowsアプリケーションでコンソール出力は邪道だとおもって、これもかなり怠慢ですが、Javaのコンソール出力をキャプチャして、最終的にMessageBoxで結果を表示させるように組み替えてみました。
#include "wjnicall.h" int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { std::basic_stringstream<_TCHAR> out; LPTSTR cmdLine = GetCommandLine(); SECURITY_ATTRIBUTES sa; HANDLE inRead, outWrite; char buf[1024]; DWORD readlen = 0; int cont = 1; memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = 0; sa.bInheritHandle = TRUE; CreatePipe(&inRead, &outWrite, &sa, 512); SetStdHandle(STD_OUTPUT_HANDLE, outWrite); JvmEnv env; env.loadArgs(cmdLine); env.addJavaParam(String(_T("-Xms8m"))); env.addJavaParam(String(_T("-Xms16m"))); env.addJavaParam(String(_T("-server"))); env.addJavaParam(String(_T("-Djava.compiler=NONE"))); String latest = env.findJvm(JRERuntimeLib+LatestVersion, false); String msg_buff; if (env.loadJLib(latest) == 0) { env.addClassPath(_T("E:\\Workspace\\Common\\Java\\JNI\\net\\classes")); if (env.createJvm() == 0) { JvmService service = JvmService(env); service.setCallClass("IfInfoTest", "net.test"); service.execute(); do { if (service.end_wait(0) == 0) { cont = 0; break; } ReadFile(inRead, buf, 1024, &readlen, NULL); if (readlen <= 0) { cont = 0; break; } buf[readlen] = 0; msg_buff.append(String((_TCHAR*)_bstr_t((char*)buf))); } while(cont); service.setReturnCodeName("return_code"); service.result(); out << _T("Execute result ") << service.getReturnCode() << _T(":") << service.getReturnMessage() << std::endl; env.destroyJvm(); } env.unloadJLib(); } MessageBox(NULL, msg_buff.c_str(), out.str().c_str(), MB_OK); return 0; }
この方がWindowsアプリケーションらしいですかねぇ・・・。
サンプルプログラムをWindowsアプリケーションで・・・
9/30に作ったJavaアプリケーション呼び出しのサンプルプログラムが結局コンソールアプリケーションだったので、それをWindowsアプリケーションで作り直してみました。
コンソールアプリケーションと違ってWindowsアプリケーションは実行時のパラメータを一つの文字列として受け渡しを行うだけでなく、実行プログラム名はパラメータに設定されないので、GetCommandLine()関数でパラメータを再取得しています。
また、このGetCommandLine()関数でもパラメータは一つの文字列として受取るので、JvmEnvクラスに新たにloadArg(LPTSTR arg, bool first_program)を追加しました。
JvmEnv.cpp(追加メソッド)
/* * プログラム起動時のパラメータを保存する * param : パラメータ(LPTSTR) * * return : 無し */ void JvmEnv::loadArgs(LPTSTR arg, bool first_program) { String org = String(arg); size_t fpos = 0, epos = String::npos; size_t argn = 0; String token; do { fpos = org.find_first_not_of(_T(" "), fpos); if (fpos != String::npos) { if (org.substr(fpos, 1) == String(_T("\""))) { fpos++; epos = org.find_first_of(_T("\""), fpos); } else { epos = org.find_first_of(_T(" "), fpos); } if (epos != String::npos) { token = org.substr(fpos, epos-fpos); epos++; } else { token = org.substr(fpos); } fpos = epos; } if (token.length() > 0) { if (first_program && (argn == 0)) caller_name = token; else { if ((token.substr(0,1) == String(_T("-"))) && (token.substr(1,1) != String(_T("-")))) { java_params.push_back(token); } else { prog_params.push_back(token); } } argn++; } } while (fpos != String::npos); }
次にサンプルのプルグラムで、
wjnicall.h
#ifndef _WIN_JNICALL #define _WIN_JNICALL #include <windows.h> #include <tchar.h> #include <sstream> #include <jvmenv.h> #include <jvmservice.h> #endif // _WIN_JNICALL
wjnicall.cpp
#include "wjnicall.h" int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { std::basic_stringstream<_TCHAR> out; LPTSTR cmdLine = GetCommandLine(); AllocConsole(); HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); JvmEnv env; env.loadArgs(cmdLine); env.addJavaParam(String(_T("-Xms8m"))); env.addJavaParam(String(_T("-Xms16m"))); env.addJavaParam(String(_T("-server"))); env.addJavaParam(String(_T("-Djava.compiler=NONE"))); String latest = env.findJvm(JRERuntimeLib+LatestVersion, false); if (env.loadJLib(latest) == 0) { env.addClassPath(_T("E:\\Workspace\\Common\\Java\\JNI\\net\\classes")); if (env.createJvm() == 0) { JvmService service = JvmService(env); service.setCallClass("IfInfoTest", "net.test"); service.execute(); service.end_wait(); service.setReturnCodeName("return_code"); service.result(); out << _T("Execute result ") << service.getReturnCode() << _T(":") << service.getReturnMessage() << std::endl; WriteConsole(hStdout, out.str().c_str(), (DWORD)out.str().length(), NULL, NULL); MessageBox(NULL, service.getReturnMessage().c_str(), _T("Result"), MB_OK); env.destroyJvm(); } env.unloadJLib(); } FreeConsole(); return 0; }
このサンプルプログラムでは、Javaのアプリケーションでコンソール(System.out)に出力される結果を表示させるために、Windowsアプリケーションにはあるまじき行為かもしれませんが、AllocConsole()関数であえてコンソールを表示させています。
Javaのプログラムを呼び出す
Javaのプログラムを呼び出す方法としては、コンソールからjava.exeを使って実行する方法が殆どで、例えばネットワークインターフェースの情報を取得するプログラムで利用したIfInfoTest.javaだと
java -cp c:\java\net\classes net.test.IfInfoTest
として起動するとか、コマンドライン実行が面倒な場合には、上記コマンドをfoo.batのようにバッチファイル化して起動する方法などが考えられます。
ただ個人的には、コマンドラインで呼び出したりバッチファイルにするのはスマートでなかったり、アイコンを定義できなかったりと不便なのと、それにJavaアプリケーションをWindowsサービスとして登録したかったこともあったので、C++のプログラムから呼び出すクラスを作ってみました。
JvmEnvクラスでは、JNIを起動する時に必要な環境の構築と情報の保持を行います。
JvmEnv.h
#ifndef _JVM_ENV_CLASS #define _JVM_ENV_CLASS #include <windows.h> #include <tchar.h> #include <comdef.h> #include <string> #include <vector> #include <map> typedef std::basic_string<_TCHAR> String; typedef std::vector<String> string_array; typedef std::map<String, String> string_map; typedef std::pair<String, String> string_pair; typedef string_map::iterator psmap; typedef std::map<String, void *> pointer_map; typedef std::pair<String, void *> pointer_pair; typedef pointer_map::iterator ppmap; #include <jni.h> typedef jint (WINAPI *lpJNI_GetDefaultJavaVMInitArgs)(void *args); typedef jint (WINAPI *lpJNI_CreateJavaVM)(JavaVM **pvm, void **penv, void *args); typedef jint (WINAPI *lpJNI_GetCreatedJavaVMs)(JavaVM **, jsize, jsize *); typedef void (WINAPI *lpPrintRoutine)(FILE *file, const char *format, va_list args); typedef void (WINAPI *lpAbortRoutine)(); typedef void (WINAPI *lpExitRoutine)(int code); // Java environment access type extern const int JDKJavaHome; extern const int JDKRuntimeLib; extern const int JREJavaHome; extern const int JRERuntimeLib; extern const int CurrentVersion; extern const int TargetVersion; extern const int LatestVersion; class JvmEnv { private: void copy(const JvmEnv& from); static String getRegValue(HKEY hkey, String valueKey, DWORD type); static void JNICALL jni_vfprintf(FILE *file, const char *format, va_list args); static void JNICALL jni_exit(jint code); static void JNICALL jni_abort(); public: JvmEnv(); JvmEnv(const JvmEnv& from); ~JvmEnv(); JvmEnv& operator=(const JvmEnv& original); void loadArgs(int argc, _TCHAR* argv[], bool first_program = true); void addProgParam(String argv); void addJavaParam(String argv); void setJLibName(String libpath); int loadJLib(String libpath = String()); int unloadJLib(); int createJvm(); JavaVM *getJvm(); jobjectArray jvmArgs(JNIEnv* jEnv); int destroyJvm(); String getCallerName(); void addClassPath(String path, String file = String()); String getClassPath(); static void setPrintRoutine(lpPrintRoutine routine); static void setAbortRoutine(lpAbortRoutine routine); static void setExitRoutine(lpExitRoutine routine); static string_map installedJvm(int type); static String findJvm(int type, bool version = true, String basic = String()); private: string_array prog_params; string_array java_params; string_array class_path; String vm_lib; String caller_name; JavaVM *jvm; JNIEnv *jni_env; HINSTANCE hjvm; lpJNI_GetDefaultJavaVMInitArgs fnJNI_GetDefaultJavaVMInitArgs; lpJNI_CreateJavaVM fnJNI_CreateJavaVM; lpJNI_GetCreatedJavaVMs fnJNI_GetCreatedJavaVMs; }; // class JvmEnv #endif // _JVM_ENV_CLASS
JvmEnv.cpp
#include "jvmenv.h" #include "jvmservice.h" const int JavaHome = 0x01; const int RuntimeLib = 0x02; const int JDK_BASE = 0x04; const int JDK = JDK_BASE; const int JDKJavaHome = (JDK_BASE+JavaHome); const int JDKRuntimeLib = (JDK_BASE+RuntimeLib); const int JRE_BASE = 0x08; const int JRE = JRE_BASE; const int JREJavaHome = (JRE_BASE+JavaHome); const int JRERuntimeLib = (JRE_BASE+RuntimeLib); const int CurrentVersion = 0x80; const int TargetVersion = CurrentVersion+0x10; const int LatestVersion = CurrentVersion+0x20; const String JAVA_ROOT = String(_T("SOFTWARE\\JavaSoft")); const String JDK_PATH = String(_T("\\Java Development Kit")); const String JRE_PATH = String(_T("\\Java Runtime Environment")); const String CURRENT_VERSION = String(_T("CurrentVersion")); const char *szJNI_GetDefaultJavaVMInitArgs = "JNI_GetDefaultJavaVMInitArgs"; const char *szJNI_CreateJavaVM = "JNI_CreateJavaVM"; const char *szJNI_GetCreatedJavaVMs = "JNI_GetCreatedJavaVMs"; #define BUFF_SIZE 256 static lpPrintRoutine print_routine = NULL; static lpAbortRoutine abort_routine = NULL; static lpExitRoutine exit_routine = NULL; /* * JvmEnvの値をコピーする * param : コピー元のデータ(JvmEnvクラス) * * return : 無し */ void JvmEnv::copy(const JvmEnv& from) { for (size_t i = 0; i < from.prog_params.size(); i++) prog_params.push_back(from.prog_params.at(i)); for (size_t i = 0; i < from.java_params.size(); i++) java_params.push_back(from.java_params.at(i)); for (size_t i = 0; i < from.class_path.size(); i++) class_path.push_back(from.class_path.at(i)); vm_lib = from.vm_lib; caller_name = from.caller_name; jvm = from.jvm; jni_env = from.jni_env; hjvm = from.hjvm; fnJNI_GetDefaultJavaVMInitArgs = from.fnJNI_GetDefaultJavaVMInitArgs; fnJNI_CreateJavaVM = from.fnJNI_CreateJavaVM; fnJNI_GetCreatedJavaVMs = from.fnJNI_GetCreatedJavaVMs; } /* * Registoryからデータを取得する * param : Registoryキー(HKEY) * データキー(String) * データタイプ(DWORD) * * return : String */ String JvmEnv::getRegValue(HKEY hkey, String valueKey, DWORD type) { DWORD buffSize = 0; DWORD valueType = type; int rc; rc = RegQueryValueEx(hkey, valueKey.c_str(), 0, &valueType, NULL, &buffSize); String result; if (rc == ERROR_SUCCESS) { buffSize++; char *buff = (char *)calloc(buffSize, sizeof(char)); if (buff != NULL) { if (RegQueryValueEx(hkey, valueKey.c_str(), 0, &valueType, (LPBYTE)buff, &buffSize) == ERROR_SUCCESS) { result = String((_TCHAR*)buff); } free(buff); } } return result; } /* * print_routineの呼出 * param : 出力ファイル(FILEポインター) * フォーマット(const charポインター) * パラメータリスト(va_list) * * return : 無し */ void (JNICALL JvmEnv::jni_vfprintf)(FILE *file, const char *format, va_list args) { if (print_routine != NULL) print_routine(file, format, args); } /* * abort_routineの呼出 * param : 無し * * return : 無し */ void (JNICALL JvmEnv::jni_abort)() { if (abort_routine != NULL) abort_routine(); } /* * exit_routineの呼出 * param : exitコード(int) * * return : 無し */ void (JNICALL JvmEnv::jni_exit)(jint code) { if (exit_routine != NULL) exit_routine(code); } // Constructer JvmEnv::JvmEnv() { jvm = NULL; jni_env = NULL; hjvm = NULL; fnJNI_GetDefaultJavaVMInitArgs = NULL; fnJNI_CreateJavaVM = NULL; fnJNI_GetCreatedJavaVMs = NULL; } // Copy Constructer JvmEnv::JvmEnv(const JvmEnv& from) { copy(from); } // Destructer JvmEnv::~JvmEnv() { } /* * "=(代入)"オペレータの処理 */ JvmEnv& JvmEnv::operator=(const JvmEnv& original) { copy(original); return *this; } /* * プログラム起動時のパラメータを保存する * param : パラメータの数(int) * パラメータ(_TCHARポインター) * * return : 無し */ void JvmEnv::loadArgs(int argc, _TCHAR* argv[], bool first_program) { for (int i = 0; i < argc; i++) { if (first_program && (i == 0)) caller_name = String(argv[i]); else { if ((argv[i][0] == _T('-')) && (argv[i][1] != _T('-'))) { java_params.push_back(String(argv[i])); } else { prog_params.push_back(String(argv[i])); } } } } /* * プログラム実行用パラメータを追加する * param : パラメータ(String) * * return : 無し */ void JvmEnv::addProgParam(String argv) { prog_params.push_back(argv); } /* * Javaの起動用パラメータを追加する * param : パラメータ(String) * * return : 無し */ void JvmEnv::addJavaParam(String argv) { java_params.push_back(argv); } /* * JavaVM Libraryの名前を指定する * param : JavaVM Libraryの名前(String) * * return : 無し */ void JvmEnv::setJLibName(String libpath) { vm_lib = libpath; } /* * JavaVM Libraryをロードし、ファンクションのアドレスを取得する * param : JavaVM Libraryの名前(String) * * return : int */ int JvmEnv::loadJLib(String libpath) { if (!libpath.empty()) { vm_lib = libpath; } else { if (vm_lib.empty()) { vm_lib = findJvm(JRERuntimeLib+LatestVersion, false); } } if (!vm_lib.empty()) { hjvm = LoadLibrary(vm_lib.c_str()); if (hjvm != NULL) { fnJNI_GetDefaultJavaVMInitArgs = (lpJNI_GetDefaultJavaVMInitArgs)GetProcAddress(hjvm, szJNI_GetDefaultJavaVMInitArgs); if (fnJNI_GetDefaultJavaVMInitArgs == NULL) { return -1; } fnJNI_CreateJavaVM = (lpJNI_CreateJavaVM)GetProcAddress(hjvm, szJNI_CreateJavaVM); if (fnJNI_CreateJavaVM == NULL) { return -2; } fnJNI_GetCreatedJavaVMs = (lpJNI_GetCreatedJavaVMs)GetProcAddress(hjvm, szJNI_GetCreatedJavaVMs); if (fnJNI_GetCreatedJavaVMs == NULL) { return -4; } } else { return -64; } } else { return -128; } return 0; } /* * JavaVM Libraryをアンロードする * param : 無し * * return : int */ int JvmEnv::unloadJLib() { if (hjvm != NULL) { if (FreeLibrary(hjvm)) { return -128; } fnJNI_GetDefaultJavaVMInitArgs = NULL; fnJNI_CreateJavaVM = NULL; fnJNI_GetCreatedJavaVMs = NULL; } hjvm = NULL; return 0; } /* * JavaVMを初期化する * param : 無し * * return : int */ int JvmEnv::createJvm() { JavaVMInitArgs vm_args; vm_args.version = JNI_VERSION_1_6; fnJNI_GetDefaultJavaVMInitArgs(&vm_args); size_t array_len = (java_params.size() + 4) * sizeof(JavaVMOption); JavaVMOption *option_array = (JavaVMOption *)malloc(array_len); memset(option_array, 0, array_len); size_t pos = 0; for (pos = 0; pos < java_params.size(); pos++) { option_array[pos].optionString = _strdup((char *)_bstr_t(java_params.at(pos).c_str())); } option_array[pos++].optionString = _strdup((char *)_bstr_t(getClassPath().c_str())); option_array[pos].optionString = "vfprintf"; option_array[pos++].extraInfo = (void *)jni_vfprintf; option_array[pos].optionString = "abort"; option_array[pos++].extraInfo = (void *)jni_abort; option_array[pos].optionString = "exit"; option_array[pos++].extraInfo = (void *)jni_exit; vm_args.version = JNI_VERSION_1_6; vm_args.options = option_array; vm_args.nOptions = (jint)(java_params.size() + 4); vm_args.ignoreUnrecognized = JNI_TRUE; int rc = fnJNI_CreateJavaVM(&jvm, (void**)&jni_env, &vm_args); for (size_t i = 0; i < java_params.size()+1; i++) { free(option_array[i].optionString); } free(option_array); if (rc != JNI_OK) return -128; return 0; } /* * JavaVMを終了する * param : 無し * * return : int */ int JvmEnv::destroyJvm() { if (jvm != NULL) { if (jvm->DestroyJavaVM() != JNI_OK) { return -128; } } jvm = NULL; return 0; } /* * JavaVMを取得する * param : 無し * * return : JavaVMポインター */ JavaVM *JvmEnv::getJvm() { return jvm; } /* * JavaVM用パラメータを取得する * param : 無し * * return : jobjectArray */ jobjectArray JvmEnv::jvmArgs(JNIEnv* jni_env) { jclass clazz = jni_env->FindClass("java/lang/String"); if (clazz == NULL) { return NULL; } size_t num = prog_params.size(); jobjectArray ja = jni_env->NewObjectArray((jsize)num, clazz, NULL); for(size_t i = 0; i < num; i++) { String c_string = prog_params.at(i); _bstr_t value = _bstr_t((_TCHAR *)c_string.c_str()); jstring j_string = jni_env->NewString((const jchar *)((wchar_t *)value), (jsize)value.length()); jni_env->SetObjectArrayElement(ja, (jsize)i, j_string); } return ja; } /* * 実行プログラム名を取得する * param : 無し * * return : String */ String JvmEnv::getCallerName() { return caller_name; } /* * クラスパスを追加する * param : クラスパス(String) * * return : void */ void JvmEnv::addClassPath(String path, String file) { if (file.empty()) class_path.push_back(path); else { String full_path = path; full_path.append(_T("\\")); full_path.append(file); class_path.push_back(full_path); } } /* * クラスパスを取得する * param : 無し * * return : String */ String JvmEnv::getClassPath() { String path; if (!class_path.empty()) { path = String(_T("-Djava.class.path=")); for (size_t i = 0; i < class_path.size(); i++) { if (i > 0) path.append(_T(";")); path.append(class_path.at(i)); } } return path; } /* * 導入済みのJava情報を取得する * param : 確認するJavaのタイプ(int) * * return : string_map */ string_map JvmEnv::installedJvm(int type) { string_map result; string_array vers; unsigned long buffSize = BUFF_SIZE; HKEY hkey = NULL; HKEY hVKey = NULL; FILETIME ft; String subkey = JAVA_ROOT; int rc; if ((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subkey.c_str(), 0, KEY_READ, &hkey)) == ERROR_SUCCESS) { RegCloseKey(hkey); switch ((int)(type&0x0c)) { case JDK: subkey.append(JDK_PATH); break; case JRE: subkey.append(JRE_PATH); break; } if ((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subkey.c_str(), 0, KEY_READ, &hkey)) == ERROR_SUCCESS) { String key = CURRENT_VERSION; String value = getRegValue(hkey, key, 0); result.insert(string_pair(key, value)); _TCHAR version[BUFF_SIZE]; for (int i = 0; ; i++) { buffSize = BUFF_SIZE; memset(version, BUFF_SIZE, sizeof(_TCHAR)); rc = RegEnumKeyEx(hkey, i, version, &buffSize, NULL, NULL, NULL, &ft); if (rc != ERROR_SUCCESS) { break; } vers.push_back(String((_TCHAR *)version)); } if (!vers.empty()) { switch (type&0x03) { case JavaHome: for (size_t i = 0; i < vers.size(); i++) { key = vers.at(i); if ((rc = RegOpenKeyEx(hkey, key.c_str(), 0, KEY_READ, &hVKey)) == ERROR_SUCCESS) { value = getRegValue(hVKey, String(_T("JavaHome")), 0); result.insert(string_pair(key, value)); RegCloseKey(hVKey); } } break; case RuntimeLib: for (size_t i = 0; i < vers.size(); i++) { key = vers.at(i); if ((rc = RegOpenKeyEx(hkey, key.c_str(), 0, KEY_READ, &hVKey)) == ERROR_SUCCESS) { value = getRegValue(hVKey, String(_T("RuntimeLib")), 0); result.insert(string_pair(key, value)); RegCloseKey(hVKey); } } break; } } RegCloseKey(hkey); } } return result; } /* * JNIのvfprintf処理にユーザ処理を加えるルーチンを登録します * param : ユーザ処理ルーチン(PrintRoutineアドレス) * * return : 無し */ void JvmEnv::setPrintRoutine(lpPrintRoutine routine) { print_routine = routine; } /* * JNIのabort処理にユーザ処理を加えるルーチンを登録します * param : ユーザ処理ルーチン(PrintRoutineアドレス) * * return : 無し */ void JvmEnv::setAbortRoutine(lpAbortRoutine routine) { abort_routine = routine; } /* * JNIのexit処理にユーザ処理を加えるルーチンを登録します * param : ユーザ処理ルーチン(ExitRoutineアドレス) * * return : 無し */ void JvmEnv::setExitRoutine(lpExitRoutine routine) { exit_routine = routine; } /* * 導入済みのJava情報から指定のパス情報を取得する * param : 確認するJavaのタイプ(int) * 取得したい情報(String) * * return : String */ String JvmEnv::findJvm(int type, bool version, String basic) { String result; string_map installed = installedJvm(type&0x0f); psmap item = installed.end(); switch (type&0xf0) { case CurrentVersion: item = installed.find(CURRENT_VERSION); if (item != installed.end()) { if (version) { result = item->second; } else { item = installed.find(item->second); if (item != installed.end()) result = item->second; } } break; case TargetVersion: for (psmap ent = installed.begin(); ent != installed.end(); ent++) { if (ent->first == basic) { if (version) { result = item->first; } else { result = item->second; } } } break; case LatestVersion: for (psmap ent = installed.begin(); ent != installed.end(); ent++) { String subkey = ent->first; if (subkey.compare(CURRENT_VERSION) == 0) { continue; } if (!basic.empty()) { if (subkey.compare(0, basic.length(), basic) != 0) { continue; } } if (item == installed.end()) { item = ent; } else { if (subkey.compare(item->first) > 0) { item = ent; } } } if (version) { result = item->first; } else { result = item->second; } break; } return result; }
JvmServiceクラスで実際にJavaのプログラムを呼び出します。
単にプログラムを呼び出して実行するだけじゃなくって、実行結果として数値(int)と文字列を取得できるように構築しました。
JvmService.h
#ifndef _JVM_SERVICE_CLASS #define _JVM_SERVICE_CLASS class JvmService { private: String jniClassPath(String original); public: JvmService(JvmEnv jvm_env); JvmService(const JvmService& from); ~JvmService(); void setCallClass(String main_class, String package = String()); String getCallClass(); int execute(); int end_wait(int wait_time = INFINITE); void result(); void setReturnCodeName(String name); int getReturnCode(); void setReturnMessageName(String name); String getReturnMessage(); static void WINAPI startApp(JvmService *svc); private: JvmEnv jvm_env; String main_class; HANDLE java_thread; String rc_field; int rc; String rm_field; String rm; }; // class JvmService #endif // _JVM_SERVICE_CLASS
JvmService.cpp
#include "jvmenv.h" #include "jvmservice.h" /* * パス分離記号の"."を"/"に変換する * param : パス(String) * * return : String */ String JvmService::jniClassPath(String original) { String path = original; size_t pos = path.find(_T('.')); do { if (pos != String::npos) { path.replace(pos++, 1, _T("/")); pos = path.find(_T('.'), pos); } } while (pos != String::npos); return path; } // Constructer JvmService::JvmService(JvmEnv jvm_env) { this->jvm_env = jvm_env; this->java_thread = NULL; this->rc = 0; } // Copy Constructer JvmService::JvmService(const JvmService& from) { jvm_env = from.jvm_env; main_class = from.main_class; java_thread = from.java_thread; rc_field = from.rc_field; rc = from.rc; rm_field = from.rm_field; rm = from.rm; } // Destructer JvmService::~JvmService() { } /* * 起動するJavaのクラスを設定する * param : クラス名(String) * パッケージ名(String) * * return : 無し */ void JvmService::setCallClass(String class_name, String package) { if (package.empty()) { main_class = class_name; } else { main_class = package; main_class.append(_T(".")); main_class.append(class_name); } } /* * 起動するJavaのクラスを取得する * param : クラス名(String) * パッケージ名(String) * * return : 無し */ String JvmService::getCallClass() { return jniClassPath(main_class); } /* * 設定したJavaのプログラムを起動する * param : 無し * * return : int */ int JvmService::execute() { java_thread = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)JvmService::startApp, (void *)this, 0, NULL); if (java_thread != NULL) { WaitForSingleObject(java_thread, 0); return 0; } return -1; } /* * Javaプログラムの終了を待つ * param : 待ち時間-秒(int) * * return : int */ int JvmService::end_wait(int wait_time) { unsigned long rc = 0; if (wait_time >= 0) rc = WaitForSingleObject(java_thread, wait_time*1000); else rc = WaitForSingleObject(java_thread, INFINITE); if (rc != WAIT_TIMEOUT) { CloseHandle(java_thread); return 0; } return -1; } /* * Javaの処理結果を取得する * param : 無し * * return : なし */ void JvmService::result() { JNIEnv *t_env; JavaVM *vm = jvm_env.getJvm(); if (vm->AttachCurrentThread((void**)&t_env, NULL) == JNI_OK) { jclass clazz = t_env->FindClass((const char *)_bstr_t((_TCHAR *)getCallClass().c_str())); if (clazz != NULL) { _bstr_t rc_str = _bstr_t((_TCHAR *)rc_field.c_str()); const char *rc_name = (const char *)rc_str; jfieldID rc_area = t_env->GetStaticFieldID(clazz, rc_name, "I"); if (t_env->ExceptionCheck() == JNI_TRUE) { if (t_env->ExceptionOccurred() != NULL) { t_env->ExceptionClear(); } } else { if (rc_area != NULL) { rc = (int)t_env->GetStaticIntField(clazz, rc_area); } } _bstr_t rm_str = _bstr_t((_TCHAR *)rm_field.c_str()); const char *rm_name = (const char *)rm_str; jfieldID rm_area = t_env->GetStaticFieldID(clazz, rm_name, "Ljava/lang/String;"); if (t_env->ExceptionCheck() == JNI_TRUE) { if (t_env->ExceptionOccurred() != NULL) { t_env->ExceptionClear(); } } else { if (rm_area != NULL) { jstring msg = (jstring)t_env->GetStaticObjectField(clazz, rm_area); const jchar *words = t_env->GetStringChars(msg, NULL); _bstr_t orginal = _bstr_t((wchar_t *)words); t_env->ReleaseStringChars(msg, words); rm = String((_TCHAR *)orginal); } } } vm->DetachCurrentThread(); } } /* * Javaの結果コードを取得するエリアの名前を設定する * param : エリアの名前(String) * * return : 無し */ void JvmService::setReturnCodeName(String name) { rc_field = name; } /* * Javaの結果コードを取得する * param : 無し * * return : int */ int JvmService::getReturnCode() { return rc; } /* * Javaの結果メッセージを取得するエリアの名前を設定する * param : エリアの名前(String) * * return : 無し */ void JvmService::setReturnMessageName(String name) { rm_field = name; } /* * Javaの結果メッセージを取得する * param : 無し * * return : String */ String JvmService::getReturnMessage() { return rm; } /* * Javaを呼び出す * param : 起動時に利用する情報(JvmServiceクラス) * * return : unsigned long */ void WINAPI JvmService::startApp(JvmService *svc) { JNIEnv *t_env; JavaVM *vm = svc->jvm_env.getJvm(); if (vm->AttachCurrentThread((void**)&t_env, NULL) == JNI_OK) { jclass clazz = t_env->FindClass((const char *)_bstr_t((_TCHAR *)svc->getCallClass().c_str())); if (clazz != NULL) { jmethodID mid = t_env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V"); if (mid != NULL) { jobjectArray ja = svc->jvm_env.jvmArgs(t_env); jthrowable tobj; t_env->CallStaticVoidMethod(clazz, mid, ja); if (t_env->ExceptionCheck() == JNI_TRUE) { if ((tobj = t_env->ExceptionOccurred()) != NULL) { t_env->ExceptionDescribe(); t_env->ExceptionClear(); } } } } vm->DetachCurrentThread(); } }
サンプルとして、ネットワークインターフェースの情報を取得するプログラムをこのクラスを使って起動するアプリケーションを作りました。
jnicall.cpp
#include <windows.h> #include <tchar.h> #include <iostream> #include <locale> #include <jvmenv.h> #include <jvmservice.h> int _tmain(int argc, _TCHAR* argv[]) { std::wcout.imbue(std::locale("japanese")); JvmEnv env; env.loadArgs(argc, argv); env.addJavaParam(String(_T("-Xms8m"))); env.addJavaParam(String(_T("-Xms16m"))); env.addJavaParam(String(_T("-client"))); env.addJavaParam(String(_T("-Djava.compiler=NONE"))); String latest = env.findJvm(JRERuntimeLib+LatestVersion, false); if (env.loadJLib(latest) == 0) { env.addClassPath(_T("c:\\java\\net\\classes")); if (env.createJvm() == 0) { JvmService service = JvmService(env); service.setCallClass(_T("IfInfoTest"), _T("net.test")); service.execute(); service.end_wait(); service.result(); std::wcout << _T("Execute result ") << service.getReturnCode() << _T(":") << service.getReturnMessage() << std::endl; env.destroyJvm(); } env.unloadJLib(); } return 0; }
結局サンプルプログラムがコンソールアプリケーションなのでちょっと変な感じですが、この部分はWindowsアプリケーションとして作ることも可能です。
RouteInfoクラスに幾つかメソッドを足しました
折角ルート情報が取れるところまで出来たので、やっぱりルート情報の追加とか削除とかも欲しくなって作ってしまいました。
- addRouteEntry(RouteInfo& entry)
- ルート情報の追加
- deleteRouteEntry(RouteInfo& entry)
- ルート情報の削除
になります。
先ずは、RouteInfo.cppに
/* * ルート情報を追加する * param : 追加するルート情報(RouteInfoクラス) * * return : bool * true = 追加できた * false = 追加できなかった */ bool RouteInfo::addRouteEntry(RouteInfo& route_info) { long retCode = NO_ERROR; MIB_IPFORWARDROW row; memset(&row, 0, sizeof(MIB_IPFORWARDROW)); row.dwForwardDest = inet_addr((const char *)_bstr_t(route_info.getDest().c_str())); row.dwForwardMask = inet_addr((const char *)_bstr_t(route_info.getMask().c_str())); row.dwForwardPolicy = 0; row.dwForwardNextHop = inet_addr((const char *)_bstr_t(route_info.getGateway().c_str())); unsigned long ifnum = route_info.getIndex(); if (ifnum == 0) { RouteInfo best = getBestRoute(route_info.getGateway(), route_info.getMask()); ifnum = best.getIndex(); } row.dwForwardIfIndex = ifnum; row.dwForwardProto = MIB_IPPROTO_NETMGMT; row.dwForwardAge = 0; row.dwForwardNextHopAS = 0; row.dwForwardMetric1 = (route_info.getMetric() > 0) ? route_info.getMetric() : 1; row.dwForwardMetric2 = ~0; row.dwForwardMetric3 = ~0; row.dwForwardMetric4 = ~0; row.dwForwardMetric5 = ~0; int metric = row.dwForwardMetric1; do { for (unsigned int type = MIB_IPROUTE_TYPE_DIRECT; type <= MIB_IPROUTE_TYPE_INDIRECT; type++) { row.dwForwardType = type; retCode = CreateIpForwardEntry(&row); if (retCode == NO_ERROR) { break; } } row.dwForwardMetric1 = ++metric; } while ((retCode != NO_ERROR) && (metric < 1024)); if (retCode == NO_ERROR) { return true; } return false; } /* * ルート情報を削除する * param : 削除するルート情報(RouteInfoクラス) * * return : bool * true = 削除できた * false = 削除できなかった */ bool RouteInfo::deleteRouteEntry(RouteInfo& route_info) { long retCode = NO_ERROR; MIB_IPFORWARDROW row; memset(&row, 0, sizeof(MIB_IPFORWARDROW)); row.dwForwardDest = inet_addr((const char *)_bstr_t(route_info.getDest().c_str())); row.dwForwardMask = inet_addr((const char *)_bstr_t(route_info.getMask().c_str())); row.dwForwardPolicy = 0; unsigned long ifnum = route_info.getIndex(); if (route_info.getGateway().empty()) { RouteInfo best = getBestRoute(route_info.getGateway(), route_info.getMask()); row.dwForwardNextHop = inet_addr((const char *)_bstr_t(best.getGateway().c_str())); ifnum = best.getIndex(); } else { row.dwForwardNextHop = inet_addr((const char *)_bstr_t(route_info.getGateway().c_str())); if (ifnum == 0) { RouteInfo best = getBestRoute(route_info.getGateway(), route_info.getMask()); ifnum = best.getIndex(); } } row.dwForwardIfIndex = ifnum; row.dwForwardProto = MIB_IPPROTO_NETMGMT; row.dwForwardAge = 0; row.dwForwardNextHopAS = 0; row.dwForwardMetric1 = 0; row.dwForwardMetric2 = ~0; row.dwForwardMetric3 = ~0; row.dwForwardMetric4 = ~0; row.dwForwardMetric5 = ~0; for (unsigned int type = MIB_IPROUTE_TYPE_DIRECT; type <= MIB_IPROUTE_TYPE_INDIRECT; type++) { row.dwForwardType = type; retCode = DeleteIpForwardEntry(&row); if (retCode == NO_ERROR) { break; } } if (retCode == NO_ERROR) { return true; } return false; }
の二つのメソッドを追加しました。
ついでにJavaにも実装して、
RouteInfo.javaクラスに
public static native boolean addRoute(RouteInfo entry); public static native boolean delRoute(RouteInfo entry);
を追加した後、いつものようにjavahでヘッダファイルを作成して、
JNIとなるjnirouteinfo.cppに
JNIEXPORT jboolean JNICALL Java_net_RouteInfo_addRoute(JNIEnv *env, jclass cls, jobject entry) { RouteInfo rt_entry = load_value(env, entry); return (jboolean)RouteInfo::addRouteEntry(rt_entry); } JNIEXPORT jboolean JNICALL Java_net_RouteInfo_delRoute(JNIEnv *env, jclass cls, jobject entry) { RouteInfo rt_entry = load_value(env, entry); return (jboolean)RouteInfo::deleteRouteEntry(rt_entry); }
を追加しました。
Javaのテストプログラムに
RouteInfo[] ris = RouteInfo.getRouteList(); for (int i = 0; i < ris.length; i++) { printRoute(ris[i]); } RouteInfo entry = new RouteInfo(2, "202.239.113.0", "255.255.255.0", "172.16.255.1", 0, 10, false, false); RouteInfo.addRoute(entry); System.out.println("After add route"); ris = RouteInfo.getRouteList(); for (int i = 0; i < ris.length; i++) { printRoute(ris[i]); } RouteInfo.delRoute(entry); System.out.println("After delete route"); ris = RouteInfo.getRouteList(); for (int i = 0; i < ris.length; i++) { printRoute(ris[i]); }
のロジックを追加してテストして完了です。