ルート情報が変更されると通知するクラスを作りました
必要にせまられてルート情報の変更を取得したいということで、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からも利用できるように実装できればいいかな。