Version 10.06
キー入力メッセージの置き換え
「今回はキー入力メッセージについて見ていきます」
『キー入力、って前にやったね』
「そう、今回は Version 9.17 ( No.178 ) で出てきた、WM_KEYDOWN や
WM_KEYUP 、そして WM_CHAR についての話」
『たしか、普通の文字は WM_CHAR でいいんだけど、それだとカーソルキー
とかは送られてこない、って話だったよね』
「逆に、 WM_KEYDOWN と WM_KEYUP だと大文字小文字が区別できない、とか
ね」
『そうそう、そんな感じ』
「で、あのとき〈これは MFC の機能〉って言ったでしょ」
『……ってことは、 SDK だけだと自分でやらなくちゃいけないってこ
と!?』
「そういうこと。というわけで、まずはこのコードを試してみて」
// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
( HWND p_hWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
)
{
if( p_uiMessage == WM_DESTROY )
{
// ×ボタンが押されました。
PostQuitMessage( 0 );
return 0;
}
else if( p_uiMessage == WM_KEYDOWN )
{
// キーが押されました。
OutputDebugString( "WM_KEYDOWN\n" );
}
else if( p_uiMessage == WM_KEYUP )
{
// キーが離されました。
OutputDebugString( "WM_KEYUP\n" );
}
else if( p_uiMessage == WM_CHAR )
{
// 文字が入力されました。
OutputDebugString( "WM_CHAR\n" );
}
// 標準的な処理をします。
return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );
}
「これをビルドして実行してから、キー入力してみて」
『ほい。お、こんな感じになったよ』
WM_KEYDOWN
WM_CHAR
WM_KEYUP
『キーが押されて WM_KEYDOWN が送られてきて、次に WM_CHAR が送られて
きて、最後にキーが離されて WM_KEYUP が送られてくるんだね。あ、でも、
カーソルキーとかだと』
WM_KEYDOWN
WM_KEYUP
『ってなるね。文字じゃないからだよね』
「そういうこと。つまり、キーが押されたときには必ず WM_KEYDOWN と
WM_KEYUP が送られてきて、もしそれが文字なら」
『 WM_CHAR も送られてくる、ってことなんだ』
「そういうこと。あ、ちなみに WM_KEYDOWN と WM_KEYUP は必ずしもペアで
送られてくるわけじゃないから」
『え、そうなの?』
「キーを押しっぱなしにしてみて」
『押しっぱなし……あ、 WM_KEYDOWN と WM_CHAR がいっぱい出てきた』
「キーリピートの時には WM_KEYDOWN と WM_CHAR が送られてきて、
WM_KEYUP は送られてこないから」
『確かにキーは上げられてないものね』
「さて。話を戻して、 WM_CHAR が送られてくる部分について」
『そうそう、そこって自分でやらなきゃいけないって言ってたよね』
「でも、今は送られてきてるよね」
『あ、ってことは、もうそういう機能は入れてあるって事?』
「そういうこと。それはこの部分」
// メッセージループ。
int MessageLoop()
{
// 略
// メッセージを変換します。
TranslateMessage( &stMsg );
// 略
}
『この TranslateMessage() って関数?』
「そう、これが WM_CHAR を作ってるんです」
『……作ってる?』
「とりあえずこれをコメントアウトして試してみて」
『ほい。ビルドして実行……あ! ホントだ、 WM_CHAR が送られてこな
い!!』
「 TranslateMessage() は WM_KEYDOWN を変換する API 。変換っていうの
とはちょっと違うかもしれないけどね」
『 WM_KEYDOWN は残ってるんでしょ?』
「そう、ちゃんとウィンドウプロシージャに送られてきてるからね。
TranslateMessage() は、 WM_KEYDOWN が送られてくると、まずそのキーが
普通の文字になるかどうか調べます」
『普通のキーじゃない、カーソルキーとかだと無視するわけね』
「そう。逆に普通の文字だったら、それを文字に変換してメッセージキュー
に WM_CHAR として送ります」
『送ったら?』
「送ったら普通に関数から抜けて、次の DispatchMessage() に移って、あ
とはウィンドウプロシージャへ。あ、えーっと」
『?』
「これは実際に見てもらった方がいいかな。このコードを試してみて」
// メッセージループ。
int MessageLoop()
{
BOOL bRes;
MSG stMsg;
// メッセージループです。
while( 1 )
{
// メッセージをキューから取り出します。
bRes = GetMessage( &stMsg, NULL, 0, 0 );
if (
( bRes == 0 )
||
( bRes == -1 )
)
{
// 終了するのでループから抜けます。
break;
}
if( stMsg.message == WM_KEYDOWN )
{
// キーが押されました。
OutputDebugString( "WM_KEYDOWN in MessageLoop\n" );
}
else if( stMsg.message == WM_KEYUP )
{
// キーが離されました。
OutputDebugString( "WM_KEYUP in MessageLoop\n" );
}
else if( stMsg.message == WM_CHAR )
{
// 文字が入力されました。
OutputDebugString( "WM_CHAR in MessageLoop\n" );
}
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
}
return stMsg.wParam;
}
『さっきのに似てる……』
「ウィンドウプロシージャの中でやってたのをメッセージループの中でして
るだけだからね。ちなみにメッセージは MSG 構造体の message ってメンバ
変数に入ってるのでそれを調べてます」
『で、ビルドして実行……あ』
WM_KEYDOWN in MessageLoop
WM_KEYDOWN
WM_CHAR in MessageLoop
WM_CHAR
WM_KEYUP in MessageLoop
WM_KEYUP
「〈in MessageLoop〉って付いてるのがメッセージループの、付いてないの
がウィンドウプロシージャのだから」
『ってことは WM_KEYDOWN が WM_CHAR に変換されても、それは置いといて
そのまま WM_KEYDOWN がウィンドウプロシージャに送られて来るんだ』
「そういうこと。 TranslateMessage() は WM_CHAR をメッセージキューに
入れるだけだから。だから、メッセージキューの中身は、まず」
WM_KEYDOWN
「ってなってて、これを GetMessage() で取り出すとメッセージキューの中
は空になります。で、 WM_KEYDOWN が TranslateMessage() に渡されると」
WM_CHAR
「と、メッセージキューに WM_CHAR が入れられます」
『でもこの時はまだ WM_KEYDOWN の処理中ってことね』
「そういうこと。 WM_KEYDOWN の処理が終わって、メッセージループの次の
周で GetMessage() で WM_CHAR が取り出されます」
『ふ、複雑……この前の WM_DESTROY とかも複雑だったけど、これも……』
「確かに〈おまじない〉として何も考えずに TranslateMessage() を入れて
もいいんだけど、こういう仕組みになってるっていうことは憶えておいて」
/*
Preview Next Story!
*/
『なんとなく、メッセージループの仕組みが分かってきたかも』
「なんとなくでいいから、イメージは持っておいてね」
『なんとなくでいいの?』
「今回のはね。次回のはちゃんと憶えておいて欲しいけど」
『ほほう』
「というわけで次回」
< Version 10.07 メッセージを待つ! >
『につづく!』
「次回は重要! ユーザーを困らせないためにもしっかり憶えて!」
『おおっ、なんか久々にそういうの出てきた!』