第192章 ホットキー・コントロール


あるキー・ストロークの組み合わせを押した時に、指定したウィンドウが アクティブになったら便利ですね。このキー・ストロークの組み合わせが ホットキーです。ホットキーを設定したり、現在設定されているホットキーを 表示したりするのに便利なコントロールがホットキー・コントロールです。 このホットキーは、システムを通じて有効なのでグローバル・ホットキー などといいます。他のアプリケーションで覆われている時、ホットキーを 押すことにより自分のウィンドウをさっとアクティブにすることができます。

多くのエディタなどでは「Ctrl + A」が「すべてを選択」に割り当てられています。 自分のウィンドウのホットキーを「Ctrl + A」に設定すると、エディタの 「Ctrl + A」の本来の動作を妨害することになるので注意が必要です。

また、エスケープ、スペース、タブキーはホットキーにすることはできません。 また、WS_CHILDスタイルのウィンドウにもホットキーをセットすることはできません。

グローバル・ホットキーに対してスレッド特異的(thread-specific)なホットキーと いうのがありますが、これについては後の章で解説します。



あるウィンドウにホットキーを設定するにはWM_SETHOTKEYを使います。

WM_SETHOTKEY wParam = (WPARAM) MAKEWORD(vkey, modifiers) lParam = 0;

vkeyはホットキーの仮想キーコードです。

modifiersはホットキーの修飾キーです。これは、 HOTKEYF_ALT(Altキー)、HOTKEYF_CONTROL(Ctrlキー)、 HOTKEYF_EXT(拡張キー)、HOTKEYF_SHIFT(Shiftキー)の 組み合わせで指定します。

ホットキーが正しくない場合は−1、ウィンドウが正しくない場合は 0、成功した時は1、すでに別のウィンドウで設定されたホットキーの場合は2 が返されます。

ウィンドウに設定されているホットキーを取得するにはWM_GETHOTKEYメッセージを 使います。

WM_GETHOTKEY wParam = 0; lParam = 0;

戻り値がホットキーの仮想キーコードおよび修飾キーとなります。 ホットキーが設定されていない場合はNULLが返されます。 戻り値の下位バイトが仮想キーコード、上位バイトが修飾キーの 組み合わせです。修飾キーの値はWM_SETHOTKEYの時と同じです。
SendMessage関数の戻り値はLRESULTなのでWORDに型キャストしてLOBYTE, HIBYTE マクロを使うと便利です。

ホットキー・コントロールの作り方は簡単です。

0.一般的なコモンコントロールの前準備   INITCOMMONCONTROLSEX構造体のdwICCメンバはICC_HOTKEY_CLASSにします。 1.CreateWindowExを使う時はクラス名をHOTKEY_CLASSにします。

たったこれだけです。

見た目はエディットコントロールと同じですがユーザーの押したキーの組み合わせを 「CTRL + GRPH + Num 6」(コントロールキー+GRPHキー+テンキーの6)という感じで 表示してくれます。

HKM_SETHOTKEY wParam = MAKEWORD(bVKHotKey, bfMods); lParam = 0;

ホットキー・コントロールにキーの組み合わせをセットします。
bVKHotKeyは仮想キーコード、bfModsは修飾キーです。値はWM_SETHOTKEY の時と同じです。

HKM_GETHOTKEY wParam = 0; lParam = 0;

ホットキー・コントロールから仮想キーコード、修飾キーを 取得します。

戻り値は16ビットで下位バイトに仮想キーコード、上位バイトに 修飾キーの組み合わせが来ます。

WM_GETHOTKEY, HKM_GETHOTKEYとWM_SETHOTKEY, HKM_SETHOTKEYは 混同しやすいので注意して下さい。ウィンドウにホットキーをセットすることと ホットキーコントロールにキーの組み合わせをセットすることは全く 別の作業です。これをうっかり混同すると非常にわかりにくいバグとなります。

さて、ホットキーとして好ましくないものをあらかじめ禁止して この組み合わせが来た時にデフォルトのキーにおきかえることも可能です。

HKM_SETRULES wParam = (WPARAM) fwCombInv; lParam = MAKELPARAM(fwModInv, 0);

このメッセージはホットキーの無効な組み合わせと、デフォルトの組み合わせをセットします。

fwCombInvは無効な組み合わせを指定します。次の中から指定します。
HKCOMB_AALTキー
HKCOMB_CCtrlキー
HKCOMB_SShiftキー
HKCOMB_CACtrl + ALTキー
HKCOMB_SAShift + ALTキー
HKCOMB_SCShift + Ctrlキー
HKCOMB_SCAShift + Ctrl + ALTキー
HKCOMB_NONE修飾されていないキー
修飾されていないキーを禁止してもテンキーとかファンクションキーなどは 単独で使用できます。

fwModInvはユーザーが無効キーの組み合わせを指定した時、 その代わりとなるキーの組み合わせです。これは、WM_SETHOTKEY の修飾キーと同じ値です。

左の図のようにユーザーのキーストロークを自動的に表示してくれます。



では、プログラムを見てみましょう。

// hotkey01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "ホットキー設定(&X)", IDM_HOT END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 127, 51 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ホットキー設定" FONT 9, "MS Pゴシック" BEGIN CONTROL "HotKey1",IDC_HOTKEY1,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,7,7,112,15 DEFPUSHBUTTON "OK",IDOK,7,30,50,14 PUSHBUTTON "キャンセル",IDCANCEL,70,30,50,14 END

普通のメニューとダイアログボックスのリソース・スクリプトです。 ホットキーコントロールはリソース・エディタで作りました。

細かなことですが、この時タブオーダーを設定してホットキーコントロールの 順番を1番にしておくと、ダイアログボックスが出た時にすぐに ホットキーを入力できるので便利です。タブオーダーを設定しないで ダイアログボックスのプロシージャのWM_INITDIALOGメッセージで SetFocus関数を処理する時は注意が必要です。この場合戻り値を FALSEにします。

タブオーダーについては第31章を参照してください。

// hotkey01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <commctrl.h> #include "resource.h" #define ID_MYHOT 1 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "hotkey01"; //ウィンドウクラス 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座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

いつもと同じですがCreateWindow関数の第2引数は特に指定していません。 あとで指定します。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; INITCOMMONCONTROLSEX ic; switch (msg) { case WM_CREATE: SetWindowText(hWnd, "ホットキー設定なし"); ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_HOTKEY_CLASS; InitCommonControlsEx(&ic); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_HOT: DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); break; } break; case WM_CLOSE: 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; }

WM_CREATEメッセージが来たらウィンドウタイトルを「ホットキー設定無し」に 設定します。そして、コモンコントロールを初期化します。

メニューで「ホットキー設定」が選択されたらダイアログボックスを 出します。

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hHot, hParent; WORD wHotKey; switch (msg) { case WM_INITDIALOG: hHot = GetDlgItem(hDlg, IDC_HOTKEY1); hParent = GetParent(hDlg); SendMessage(hHot, HKM_SETRULES, (WPARAM)HKCOMB_NONE, MAKELPARAM(HOTKEYF_ALT, 0)); wHotKey = (WORD)SendMessage(hParent, WM_GETHOTKEY, 0, 0); SendMessage(hHot, HKM_SETHOTKEY, (WPARAM)wHotKey, 0); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: wHotKey = (WORD)SendMessage(hHot, HKM_GETHOTKEY, 0, 0); if (SendMessage(hParent, WM_SETHOTKEY, (WPARAM)wHotKey, 0) != 1) { MessageBox(hDlg, "設定失敗です", "失敗", MB_OK); return TRUE; } if (wHotKey) { SetWindowText(hParent, "ホットキー設定済"); } else SetWindowText(hParent, "ホットキー設定なし"); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

ダイアログボックスのプロシージャです。

WM_INITDIALOGメッセージが来たら、ホットキー・コントロール、 親ウィンドウのハンドルをスタティック変数に取得しておくと便利です。 次にホットキー・コントロールの無効なキーの組み合わせとデフォルトの 組み合わせを指定しておきます。そして、現在親ウィンドウに関連付けられている ホットキーを取得して、それをホットキー・コントロールに表示します。 WM_***とHKM_***メッセージを混同しないように注意して下さい。

OKボタンが押されたらホットキー・コントロールに設定してある キーの組み合わせを取得して親ウィンドウに関連付けます。 この場合戻り値が2(すでに他のウィンドウでホットキーとして使われている) となることはありません。なぜならば、他のウィンドウで使われている キーの組み合わせを押すと、そのウィンドウが現れてしまい、 そのキーの組み合わせをコントロールに表示できないからです。 したがって戻り値が1以外はエラー扱いです。
ホットキーをウィンドウに関連付けたら親ウィンドウのタイトルを 変更します。

今回もたいして難しくはありませんでした。くれぐれもWM_***とHKM_*** メッセージを混同しないよう気を付けて下さい。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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