メッセージフック
他のアプリケーションに送られているメッセージを監視したいと考えたことがあると思います。
しかし、Win32環境では、普通は他のプロセスへ送られているメッセージを知ることはできません。
そこで使うのが、SetWindowsHookExというAPIです。
このAPIを使えば、メッセージフックを実現できます。
定義は、次のようになっています。
フックを解除するときには、UnhookWindowsHookExを使います。
引数hhookには、SetWindowsHookExが返すハンドルを渡します。
実装方法を簡単に説明します。
DLLを作って、その中にフックプロシージャを記述します。 例えば、次のようになります。
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) { LPCWPSTRUCT pcwps; pcwps = (LPCWPSTRUCT)lParam; if(nCode == HC_ACTION) { //何らかの処理 } return CallNextHookEx(hHook, nCode, wParam, lParam); // hHook : 保存しておいたフックプロシージャのハンドル }
注意しなければならないのは、フックプロシージャがプロセスをまたがって動くということです。
本体アプリケーションへの通知が必要になった場合、そこで本体アプリケーションとのプロセス間通信が必要になるわけです。
私は、アプリケーションのウィンドウにメッセージを送ったりしています。
ミューテックスオブジェクトなどの同期オブジェクトを使った同期を考えなければいけないことがありますが、ここではとりあげません。
フックプロシージャが別のプロセスで動くため、フックプロシージャを実行中にグローバル変数をアクセスするときには、よく考えなければなりません。
ここでは、hHookがグローバル変数です。
ここで、アプリケーションがロードされ実行されるときに、Windowsが何をしているのかを、簡単に説明します。
WindowsはマルチタスクのOSです。そのため、1つのプログラムがいろいろなプロセスで同時に実行されていることがあり得ます。
しかし、プログラムコードをプロセス毎に用意しては、メモリが無駄になります。
そこで、データ領域だけをプロセス毎に用意し、プログラムコードは共有します。プログラムコードは共通だからです。
DLLも例外ではありません。
そして、フックプロシージャはプロセスにまたがって動きます。
ですから、フックプロシージャからDLLのデータ領域(例えばhHook)にアクセスするときには、
別のプロセスの領域にアクセスすることになり、データを共有領域に置かないとうまくいきません。
ハンドルを保存するためのhHookは共有領域に置きたいので、DLLのコードに次のような記述をします。
#pragma data_seg(".sharedata") HHOOK hHook = NULL; // フックプロシージャのハンドル #pragma data_seg()さらに、DLLのDEFファイルに次のような記述をして下さい。DEFファイルが無ければ、作成し、プロジェクトに追加して下さい。
LIBRARY SECTIONS .sharedata READ WRITE SHARED
次は、フックプロシージャのインストールです。
私は、フックの開始をする関数をDLL内部においています。例えば、HookStart()という関数をDLLに用意して、アプリケーション本体から呼んでいます。
void HookStart() { hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hInstance, 0); }
などとします。実際には、成功か失敗かを返す方がいいでしょうね。
ここで、hInstanceとあるのは、DLLのインスタンスハンドルです。
最後は、フックプロシージャのアンインストールです。
void HookEnd() { UnhookWindowsHookEx(hHook); }
最後に、注意点です。
フックプロシージャ内で、あまり時間のかかる処理はしてはいけません。
時間のかかる処理は、アプリケーション本体のプロセスで行って下さい。
参考文献:「Windows95 APIバイブル (Windows95 Win32 Programing API Bible)1 Win32編」
E-Mail:wbs03748@mail.wbs.ne.jp
質問・感想などのメールはこちらへ。