キーボードイベント
システムメッセージ
さて、ここではキーボードの処理を説明しましょう
キーボードイベントはウィンドウズのイベント処理の基本を学習できますし
他のアプリケーションとのイベントの関係も把握することができるでしょう
キーボードイベントは、キーボードが押された時点で発生しますが
それは直接メッセージキューに入るわけではないのです
キーボードイベントはまずシステムメッセージキューという場所に格納されます
理由は単純で、メッセージの同期をとるためです
キーボードイベントはアプリケーションがそれを処理するよりも速い速度でタイプすることができます
しかし、基本的にキーボードイベントはアクティブなウィンドウに対して送られますが
キューのメッセージの途中で、アクティブなウィンドウが切りかえられた場合
その後のキーイベントは切り替わったウィンドウに送られるべきです
これを行うために、まずシステムメッセージキューにキーイベント(キーストローク)をセットし
次のメッセージを送り先アプリケーションのメッセージキューにセットします
キーボードイベントは、基本的にアクティブなウィンドウとその子コントロールに有効です
コントロールとは、後に説明しますがウィンドウを含もボタンやラジオボタンなど
GUIを形成するWindowsの部品を指します(Javaシステムのコンポーネントにあたります)
子のような、キーボードからのメッセージを受け取る権利を入力フォーカスとも呼びます
キーボードのキーが押されると、入力フォーカスを持つプログラムのメッセージキューに
押された場合はWM_KEYDOWNが、離した時はWM_KEYUPがポストされます
Windowsは、キーボードのキーを仮想キーコードとして扱います
デバイスに依存する数値ではなく、仮想コードを定義して論理的に扱います
仮想キーコードの値は、ウィンドウプロシージャの第三引数 WPARAM に格納されています
これらのメッセージは、0を返します
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
TCHAR tcStr[128];
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
wsprintf(tcStr , "Kitty on your lap\nwPalam = %Xh" , wp);
MessageBox(NULL , tcStr , TEXT("Key Event") , MB_OK);
return 0;
}
return DefWindowProc(hwnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hwnd = CreateWindow(
TEXT("KITTY") , TEXT("Kitty on your lap") ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hwnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
return msg.wParam;
}
アプリケーションウィンドウをアクティブに子、入力フォーカスを得て下さい
その状態で何らかのキーボードのキーを押すと、ダイアログが表示されます
ダイアログの2行目は仮想キーコードを表示しています
仮想キーコードにはそれぞれ定数も割り当てられていますが、今は使いません
仮想キーコードを手動で文字に変換するような手間をかけるプログラマはいないでしょう
//文字の取得の方法は、後ほど説明します
WM_KEYDOWN や WM_KEYUP は、LPARAM にはその他の情報が入力されており
少し複雑ですが、32の各ビットで6つのフィールドに分かれて情報を格納しています
最も重要なのは 0〜15ビット のリピート情報と呼ばれる情報で、通常は1です
プログラムの処理速度以上の速さでキーイベントが発生している場合は1より大きな数になります
例えば、キーを押しつづけた場合はWindowsに設定されている速度で連続にKEY_DOWNが発生します
しかし、処理が間に合わない場合はWindowsはメッセージをまとめてリピートを変わりにセットします
これを無視するか処理するかはプログラマの責任になります
リピート情報が1以外になるのは押した時のメッセージのみで、離した時のメッセージは常に1です
16〜23ビットまでは OEMスキャンコード と呼ばれるものです
これは低水準な分野のコードなので、この場では説明しません
一般的にこの値は無視します
24ビット目の値は 拡張キーフラグ です
IBM拡張キーボードで追加されたキーストロークなら1にセットされます
キーボード右側のAltやCtrl、テンキー以外の方向キーなどの場合は1になります
29ビット目には コンテキストコードというものがセットされます
これはAltが押されている場合は1になります
30ビット目は直前のキーの状態を意味します
直前に発生したキーイベントが離されたものであれば0
押された状態のものならば1であり、キーを離した時の状態はつねに1になります
直前のキーストロークが押しつづけて発生したオートリピートの監視に使えます
最後の31ビット目は、キーが押されたものであれば0、離されたものであれば1になります
これは、WM_KEYDOWNでは0、WM_KEYUPなら1になっています
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
static int iWidth = 10;
int iCount;
TCHAR tcRe[4];
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
hdc = GetDC(hwnd);
iWidth++;
SelectObject(hdc , GetStockObject(WHITE_PEN));
Rectangle(hdc , 0 , 0 , 400 , 400);
wsprintf(tcRe , "%d" , (short)lp);
TextOut(hdc , 10 , 150 , tcRe , lstrlen(tcRe));
SelectObject(hdc , GetStockObject(BLACK_BRUSH));
for(iCount = 0 ; iCount < 10000 ; iCount++)
Rectangle(hdc , 10 , 10 , iWidth , 100);
ReleaseDC(hwnd , hdc);
return 0;
}
return DefWindowProc(hwnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hwnd = CreateWindow(
TEXT("KITTY") , TEXT("Kitty on your lap") ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hwnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
return msg.wParam;
}
何らかのキーを押すと、黒い棒が右に伸びていきます
このとき、矩形を描画するのに無意味に一万回重ねて描画しています
そのため、処理に時間がかかりリピート回数が1以上になります
もし1以外にならなければループ回数を増やし、動かなくなるようであれば減らしてください
ところで、気がついた方もいるかもしれませんが Alt の場合はイベントが発生しません
Altキーは、WM_KEYDOWN や WM_KEYUP を発行していないのです
なぜならば、これはアプリケーションよりもWindowsシステムに重要なキーストロークだからです
たとえば、Alt + Tab でアクティブウィンドウを切り替えたりすることができますね
しかし、このようなシステム側のメッセージを受けることもできます
押された時はWM_SYSKEYDOWN、離した時はWM_SYSKEYUPです
これは Alt と他のキーで生成されるメッセージです
それ以外は WM_KEYDOWN と WM_KEYUP と同じです
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SYSKEYDOWN:
MessageBox(NULL , TEXT("Kitty on your lap") , TEXT("Key Event") , MB_OK);
return 0;
}
return DefWindowProc(hwnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hwnd = CreateWindow(
TEXT("KITTY") , TEXT("Kitty on your lap") ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hwnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
return msg.wParam;
}