技術ノート:ダイアログボックスでWM_KEYDOWNを受け取る
Visual C++プログラムにおいて ごくごく基本的なWM_KEYDOWNメッセージだが、 実はダイアログボックスの場合はこのメッセージが来ない。
WM_KEYDOWNの対処というのが初歩の技術なので ダイアログ上でも安易に使いたくなるのだが、 実際やってみるとメッセージが来ないもので 初めて体験すると結構驚く。
というのも、ダイアログボックスは エディットボックスやボタンなどのコントロールを配置すると キーに関しては半自動的に反応するし、 TABキーを押すとフォーカスが移るし、 Enterキーを押すとOKボタンが押された代わりになる。
こういう処理を内部でやってくれる代わりに キーに関する情報はすべて内部で処理されてしまって ダイアログのプロシージャにはWM_KEYDOWNが飛んでこないのだ。
しかし、独自のキー操作処理を導入したいこともあり、 情報が来ないと非常に辛い。
そこで処理を「フック」する。 フックの名の通り、ダイアログボックスで内部処理される情報を 途中で「引っ掛ける」のだ。 フックを仕掛けておけば、該当する情報を一旦受け取れる。 その中から必要な情報に対して処理をし、 そうでない情報はそのまま通過させるのだ。
HHOOK hHook;
フックのハンドルを宣言しておく。
そしてWM_INITDIALOGの中などで
hHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc, NULL,GetWindowThreadProcessId(hDlg,NULL));
とし、フックを仕掛ける。 第1引数にWH_KEYBOARDを指定するとキーに関する情報が 第2引数で指定したKeyboardProc関数に飛んでいく。
KeyboardProcは以下の形で記述しておく。
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam, LPARAM lParam) { //0未満のときはCallNextHookEx関数にメッセージを渡し、 //CallNextHookExが返した値を返さなければならない if(nCode < 0){ return CallNextHookEx(hHook,nCode,wParam,lParam); }
//キーに関する情報が来たなら if(nCode==HC_ACTION){ //キーの情報をダイアログボックスに送ってやる
//キーの遷移状態のビットをチェックして //WM_KEYDOWNとWM_KEYUPを区別する if((lParam & 0x80000000)==0){ SendMessage(hDlg,WM_KEYDOWN,wParam,lParam); }else{ SendMessage(hDlg,WM_KEYUP,wParam,lParam); } }
return CallNextHookEx(hHook,nCode,wParam,lParam); }
この関数の中で、キーに関する情報が通過する際に 目的のダイアログにSendMessageでWM_KEYDOWNメッセージを送れば ダイアログプロシージャの方で対処ができる。
勝手にキー情報をつぶしてしまうと 本来、ダイアログ内部で処理されるキーまで反応しなくなるので CallNextHookExを使って、受け取った情報を再度流してやる。
これでダイアログプロシージャの方では ほぼ通常通りの処理が書ける。
case WM_KEYDOWN: switch(wParam){ case VK_DELETE: //自分にフォーカスがあるときだけ反応する if(GetFocus()==GetDlgItem(hDlg,IDC_LIST1)){ //やりたい処理を書く } break; } break;
場合によっては、反応するコントロールに フォーカスがあるかどうかをチェックした方が綺麗な結果になる。
なお、対象のダイアログボックスを閉じるとき、 つまりEndDialogなどを行うときに
UnhookWindowsHookEx(hHook);
としてフックを解除しておく。
ダイアログボックスでキーを使うという需要は高いだろうに 単純なようで意外と手間のかかるこの処理。 同じ問題でつまづくプログラマは多いと思うので ここに情報を残しておく。
|