第40章 ChooseFontとフック関数


今回は、コモンダイアログボックスの一つである ChooseFontを作り替える方法について解説します。 大して難しくはありません。フック関数を使います。 フックとは鈎のことです。ピーターパンに出てくるフック船長のあのフックです。 ちょっと引っかけるという意味です。 まず、フック関数で作り替えたダイアログボックスを見てみましょう。
上が、第38章第39章で作成したデフォルトのダイアログボックスです。 下が、今回作成するフック関数で修正したダイアログボックスです。 どこが違うかよーく見てください。

まず、表題というか題名というかいろいろ変わっています。 それと、「フォント名(F)」のFに注目してください。 これは、Alt(GRPH)キーを押して、Fを押すとここの項目選択 にこれますよ、という意味です。このFも下のダイアログボックスでは、 Pに変わっています。

さらに、見た目ではわかりませんが機能も変更が加えられています。 「やめる」ボタンを押すと次のようなメッセージボックスが出て 本当にフォントの指定をやめるかどうかを確認します。

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; }

ここも、いつもと同じです。親ウィンドウに表示されるタイトルを少し変えてみました。

//ウィンドウプロシージャ 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; }

ここもほとんど変わっていません。WM_PAINTのところで 表示する文字列をちょっとだけ変えただけです。

//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; }

CF_ENABLEHOOKを加えたのと、フック関数のアドレスを指定しています。 フック関数のアドレスは、必ず(LPCFHOOKPROC)で型キャストしてください。 最近のコンパイラは型キャストしていないと怒りだします。

//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; }

このダイアログボックスが作られるとすぐにSetWindowText関数で表題を変更しています。 SetWindowText関数については、第11章第20章第21章などを 参照してください。また、文字列の(&F)などを変更するだけで、その機能も変更されます。

これと、似たような関数にSetDlgItemText関数があります。 これは、第15章で少しだけ解説をしてあります。

ところで、このダイアログボックスの各コントロールのIDはどうしてわかったの?

はい。コモンダイアログボックスのコントロールのIDは、スタティックコントロールなら stc1, stc2, stc3,...グループは、grp1, grp2, grp3,...ボタンはIDOK, IDCANCEL、 コンボボックスは、cmb1, cmb2,..リストボックスは、lst1,..、チェックボックスは chx1,...、などというように決められています。

さて、自前でメッセージを処理して本来の仕事はしなくてよい場合は、 TRUEを返します。本来の仕事をしてもらいたいときは、FALSEを返します。 したがって、上の例ではボタンが押されると(WM_COMMANDが来た)その種類を 調べてキャンセルボタンならメッセージボックスを出します。それ以外の ボタンであったらFALSEを返して本来の仕事をしてもらいます。 また、メッセージボックスの返事がOKならば、FALSEを返して 本来のキャンセルボタンの仕事をしてもらいます。 メッセージボックスの返事がキャンセルならTRUEを返して、本来の 仕事を無視してもらいます。このへんは、うっかりすると 逆にしてしまいがちです。いろいろやってみるとわかります。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

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