第163章 WH_CALLWNDPROCフック


WH_CALLWNDPROCはSendMessage関数によって送られるメッセージをフックします。 今回は、これを利用してウィンドウが閉じられようとした時に メッセージボックスを出すプログラムを作ります。

// hook03x.h #define EXPORT extern "C" __declspec(dllexport) EXPORT BOOL SetMainHWND(HWND); EXPORT BOOL StartHook(void); EXPORT BOOL EndHook(void); EXPORT void MenuCheck(void); EXPORT BOOL IsEndOK(void); EXPORT LRESULT CALLBACK MyHookProc(int, WPARAM, LPARAM);

dllのヘッダファイルです。

// hook03x.cpp hook03x.dll #include <windows.h> #include "hook03x.h" HINSTANCE hInst; HHOOK hHook; HWND hWnd; BOOL bHook = FALSE; int WINAPI DllMain(HINSTANCE hInstance, DWORD fdReason, PVOID pvReserved) { hInst = hInstance; return TRUE; }

前回とほぼ同じです。

EXPORT BOOL StartHook() { hHook = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)MyHookProc, hInst, 0); if (hHook == NULL) { MessageBox(hWnd, "フックに失敗しました", "Error", MB_OK); return FALSE; } else { MessageBox(hWnd, "フックに成功しました", "OK", MB_OK); bHook = TRUE; } return TRUE; }

DllMainで取得したこのdllのインスタンスハンドルを使ってSetWindowsHookEx関数を 実行しています。hWndは呼び出し側からもらった親のウィンドウハンドルです。 フックのインストールにに成功したらbHookをTRUEにしておきます。

EXPORT BOOL EndHook() { if (UnhookWindowsHookEx(hHook) == 0) { MessageBox(hWnd, "フック解除に失敗しました", "Error", MB_OK); return FALSE; } else { MessageBox(hWnd, "フックの解除成功", "OK", MB_OK); bHook = FALSE; } return TRUE; }

フックの解除です。成功したらbHookをFALSEにしておきます。

EXPORT BOOL SetMainHWND(HWND hMyWnd) { if (hMyWnd == NULL) { MessageBox(NULL, "HWNDが正しくない", "Error", MB_OK); return FALSE; } hWnd = hMyWnd; return TRUE; }

呼び出し側からウィンドウハンドルをもらう関数です。

EXPORT void MenuCheck() { HMENU hMenu, hSubMenu; hMenu = GetMenu(hWnd); hSubMenu = GetSubMenu(hMenu, 1); if (bHook) { EnableMenuItem(hSubMenu, 0, MF_BYPOSITION | MF_GRAYED); EnableMenuItem(hSubMenu, 1, MF_BYPOSITION | MF_ENABLED); } else { EnableMenuItem(hSubMenu, 0, MF_BYPOSITION | MF_ENABLED); EnableMenuItem(hSubMenu, 1, MF_BYPOSITION | MF_GRAYED); } return; }

さて、今回はメニュー項目のグレー表示などもdll側でやってしまいます。 この場合、MF_BYCOMMANDは使えないのでMF_BYPOSITIONで操作しています。

EXPORT BOOL IsEndOK() { if (bHook) { return FALSE; } else { return TRUE; } }

呼び出し側のプログラムを終了してよいかどうかを調べる関数です。 bHookがTRUEならフックがインストールされているのでFALSEを返します。 呼び出し側プログラムを終了してもよい時(bHookがFALSEの時)は TRUEを返しています。

EXPORT LRESULT CALLBACK MyHookProc(int nCode, WPARAM wp, LPARAM lp) { CWPSTRUCT *pcwp; char str[256]; if (nCode < 0) { return CallNextHookEx(hHook, nCode, wp, lp); } if (nCode == HC_ACTION) { if (wp == NULL) { pcwp = (CWPSTRUCT *)lp; if (pcwp->message == WM_CLOSE) { MessageBeep(MB_OK); wsprintf(str, "Window %X が閉じられようとしています", pcwp->hwnd); MessageBox(pcwp->hwnd, str, "OK", MB_OK); } } } return CallNextHookEx(hHook, nCode, wp, lp); }

今回のフックプロシージャです。 nCodeがマイナスの時は直ちにCallNextHookEx関数の戻り値を返します。 nCodeがHC_ACTIONの時はメッセージの処理をします。メッセージを 改変することはできません。戻り値はCallNextHookExの戻り値にします。 もしくは0にします。(前者を強く推奨するとヘルプに書いてあります)

wpにはもし、現在のスレッドから送られたメッセージなら0以外、他のスレッドから 送られてきたものならNULLが入ります。

lpにはCWPSTRUCT構造体へのポインタが入ります。

typedef struct tagCWPSTRUCT { // cwps LPARAM lParam; WPARAM wParam; UINT message; HWND hwnd; } CWPSTRUCT;

これは、フックしたメッセージの中身ですね。 今回は、WM_CLOSEメッセージをフックします。

では、呼び出し側のプログラムを見てみましょう。

// hook03.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "フック(&H)" BEGIN MENUITEM "フック開始(&S)", IDM_HOOKSTART MENUITEM "フック終了(&E)", IDM_HOOKEND END END

いつものメニューのリソース・スクリプトです。

// hook03.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include "resource.h" #include "hook03x.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "hook03"; //ウィンドウクラス HINSTANCE hInst; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるフック",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 190,//幅 75, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_INITMENU: MenuCheck(); break; case WM_CREATE: SetMainHWND(hWnd); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_HOOKSTART: StartHook(); break; case IDM_HOOKEND: EndHook(); break; } break; case WM_CLOSE: if (!IsEndOK()) { MessageBox(hWnd, "フックが解除されていません", "注意", MB_OK); break; } id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

呼び出し側は、たいして難しくないので一度に表示しました。 呼び出し側ではメニューの心配とか、終了時にフックが解除されているか どうかのめんどうはあまり見いてません。dll側の関数に任せています。 今回は、前回と比べてあまり変わり映えのしないプログラムでした。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 14/Dec/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。