ローカルフックは実行したプロセス内のみフックするのに対して、システムフックはプロセスを超えてフックができる。
基本的な処理フローはこんな感じ。
- フックプロシージャをDLL側で作る
HHOOKやスタティック変数をDLL間で共有する(#pragma data_seg)
共有領域を設定するため.defファイルを作る
.defファイルはプロジェクトの「プロパティ」ー「リンカ」ー「入力」ー「モジュール定義ファイル」に名前を登録する - SetWindowsHookExでフックプロシージャを登録
第三引数はDLLのハンドルをセット(ここがローカルフックとは違う!) - フックプロシージャでフック処理
CallNextHookExで次のフックへ引き継ぎ - UnhookWindowsHookExで解除
2,3,4はほとんどローカルフックと同じで、SetWindowsHookExにDLLのハンドルを渡す点だけが違う。1が主にシステムフックに必要な設定となる。書いてあるとおりDLLで作るのと最低限HHOOKを共有領域として設定して、.defファイルを設定する必要がある。キーボードフックなどでキーの値を取っておく場合は共有領域で宣言しておく必要がある。
DLL側のコード
#define DLL_API extern "C" __declspec( dllexport ) // 共有領域 #pragma data_seg(".shareddata") HHOOK hMyHook=0; #pragma data_seg() HMODULE hInst; BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if(ul_reason_for_call==DLL_PROCESS_ATTACH){ hInst = hModule; } return TRUE; } LRESULT CALLBACK MyHookProc(int nCode, WPARAM wParam, LPARAM lParam) { // nCodeがマイナスの場合は何もしないですぐに返す。そういう決まり。 if(nCode < 0){ return CallNextHookEx(hMyHook, nCode, wParam, lParam); } // any hook return CallNextHookEx(hMyHook, nCode, wParam, lParam); } DLL_API bool BeginHook(){ hMyHook = SetWindowsHookEx( WH_KEYBOARD, // フックタイプ (HOOKPROC)MyHookProc, // フックプロシージャのアドレス hInst, // ローカルフックではNULL。システムフックではDLLのハンドル。 0); // フックされるスレッド if(hMyHook == NULL){ return false; } return true; } DLL_API void EndHook(){ if(UnhookWindowsHookEx(hMyHook) == 0){ MessageBoxA(NULL, "Failed to delete hook!", "Error", MB_OK); } }
.defファイル
SECTIONS .shareddata READ WRITE SHARED
実行ファイル側
// dll関数を取得する関数ポインタタイプ typedef bool (*BeginHookFunc)(void); typedef void (*EndHookFunc)(void); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // ウィンドウの生成など // dll load HMODULE hModule = LoadLibrary(TEXT("HookDll.dll")); assert(hModule != NULL && "dll load error"); { // dll関数ポインタ取得 BeginHookFunc BeginHook = (BeginHookFunc)GetProcAddress(hModule ,"BeginHook"); assert(BeginHook != NULL && "dll func call error"); EndHookFunc EndHook = (EndHookFunc)GetProcAddress(hModule ,"EndHook"); assert(EndHook != NULL && "dll func call error"); BeginHook(); // メイン メッセージ ループ: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } EndHook(); } // dll release FreeLibrary(hModule); return (int) msg.wParam; }
以上のようにやっていることはローカルフックとほとんど同じだ。実行ファイル側でDLLを読み込んで関数ポインタを取得している部分が違うくらいで、他はローカルフックと同じでメインループの前後でBeginHook(),EndHook()してやればいい。
これでキーボードフックまでの下準備が整った。
システムフック参考
http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/08.html
http://www.kumei.ne.jp/c_lang/sdk2/sdk_161.htm