さらに、見た目ではわかりませんが機能も変更が加えられています。
「やめる」ボタンを押すと次のようなメッセージボックスが出て
本当にフォントの指定をやめるかどうかを確認します。
OKなら、フォント選択を中止にして、ダイアログボックスが
消えます。キャンセルなら引き続きフォントの設定が続けられます。
では、このようなことのできるフック関数を使うにはどうすれば
いいのでしょうか。
作り方は簡単です。
たったこれだけです。フック関数の引数や戻り値はプロシージャと同じです。 では、早速プログラムを見てみましょう。1.CHOOSEFONT構造体のlpfnHookメンバにフック関数の アドレスを指定します。 2.FlagsメンバにCF_ENABLEHOOKを加えます。 3.プロシージャと同じ要領でフック関数を記述します。
ここまでは、いつもと大して変わりません。// font04.cpp ChooseFontとフック関数 #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyHook(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE, LPCSTR); BOOL InitInstance(HINSTANCE, LPCSTR, int); int setcf(CHOOSEFONT *); int draw_on = 0; CHOOSEFONT cf; LOGFONT logfont; HWND hParent; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; char szClassName[] = "font04"; //ウィンドウクラス if (!hPrevInst) { if (!InitApp(hCurInst, szClassName)) return FALSE; } if (!InitInstance(hCurInst, szClassName, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
ここも同じです。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst, LPCSTR szClassName) { WNDCLASS wc; 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 = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); }
ここも、いつもと同じです。親ウィンドウに表示されるタイトルを少し変えてみました。//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, LPCSTR szClassName, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるフック関数", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hParent = hWnd; strcpy(logfont.lfFaceName, "MS ゴシック"); setcf(&cf); return TRUE; }
ここもほとんど変わっていません。WM_PAINTのところで 表示する文字列をちょっとだけ変えただけです。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { static draw_on = 0; int id; HDC hdc; PAINTSTRUCT ps; HFONT hFont, hOldf; COLORREF crOldc; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0L, 0L); break; case IDM_FONT: if(ChooseFont(&cf) ==TRUE) { InvalidateRect(hWnd, NULL, TRUE); draw_on = 1; } break; } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); hFont = CreateFontIndirect(cf.lpLogFont); hOldf = SelectObject(hdc, hFont); crOldc = SetTextColor(hdc, cf.rgbColors); if(draw_on) TextOut(hdc, 10, 10, (LPCTSTR)"猫でもわかるフック関数", 22); SelectObject(hdc, hOldf); SetTextColor(hdc, crOldc); DeleteObject(hFont); EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, (LPCTSTR)"終了してもよいですか", (LPCTSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }
CF_ENABLEHOOKを加えたのと、フック関数のアドレスを指定しています。 フック関数のアドレスは、必ず(LPCFHOOKPROC)で型キャストしてください。 最近のコンパイラは型キャストしていないと怒りだします。//CHOOSEFONT構造体の設定 int setcf(CHOOSEFONT *cf) { cf->lStructSize = sizeof(CHOOSEFONT); cf->hwndOwner = hParent; cf->lpLogFont = &logfont; cf->Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_ENABLEHOOK; cf->rgbColors = RGB(0, 0, 0); cf->nFontType = SCREEN_FONTTYPE; cf->lpfnHook = (LPCFHOOKPROC)MyHook;//フック関数を指定 return 0; }
このダイアログボックスが作られるとすぐにSetWindowText関数で表題を変更しています。 SetWindowText関数については、第11章、 第20章、第21章などを 参照してください。また、文字列の(&F)などを変更するだけで、その機能も変更されます。//ChooseFontをフック LRESULT CALLBACK MyHook(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_INITDIALOG: SetWindowText(hWnd, "猫でもわかるフック"); SetWindowText(GetDlgItem(hWnd, IDOK), "決定"); SetWindowText(GetDlgItem(hWnd, IDCANCEL), "やめた"); SetWindowText(GetDlgItem(hWnd, grp2), "見本です"); SetWindowText(GetDlgItem(hWnd, grp1), "飾りです(&K)"); SetWindowText(GetDlgItem(hWnd, stc1), "Font(&P)"); SetWindowText(GetDlgItem(hWnd, stc2), "Style(&S)"); SetWindowText(GetDlgItem(hWnd, stc3), "Size(&Z)"); //SetWindwoTextもSetDlgItemTextもここでは同じ働きです SetDlgItemText(hWnd, stc4, "Color(&I)"); return TRUE; case WM_COMMAND: if (LOWORD(wp) == IDCANCEL) { if(MessageBox(hWnd, (LPCTSTR)"本当にやめますか", (LPCTSTR)"中止", MB_OKCANCEL) == IDOK) { return FALSE;//FALSEを返すと本来の機能 } else { return TRUE;//TRUEを返すと本来の機能は無視 } } break; default: return FALSE; } return FALSE; }
これと、似たような関数にSetDlgItemText関数があります。 これは、第15章で少しだけ解説をしてあります。
さて、自前でメッセージを処理して本来の仕事はしなくてよい場合は、
TRUEを返します。本来の仕事をしてもらいたいときは、FALSEを返します。
したがって、上の例ではボタンが押されると(WM_COMMANDが来た)その種類を
調べてキャンセルボタンならメッセージボックスを出します。それ以外の
ボタンであったらFALSEを返して本来の仕事をしてもらいます。
また、メッセージボックスの返事がOKならば、FALSEを返して
本来のキャンセルボタンの仕事をしてもらいます。
メッセージボックスの返事がキャンセルならTRUEを返して、本来の
仕事を無視してもらいます。このへんは、うっかりすると
逆にしてしまいがちです。いろいろやってみるとわかります。
[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]
Update Jun/02/1997 By Y.Kumei