Version 10.10
アクセラレーターふたたび
「前回のメニューに続いて、今回はアクセラレーターを使ってみます」
『 Version 9.07 ( No.168 ) でやったのだね』
「あのときのまとめをすると、」
・アクセラレーターはリソースのひとつ。
いわゆる「ショートカットキー」のこと。
・操作は HACCEL というアクセラレーターハンドルを使う。
MFC だと CMainFrame::m_hAccelTable というのを持ってる。
・操作用 API には
CopyAcceleratorTable() : コピー。
CreateAcceleratorTable() : 新規作成。
DestroyAcceleratorTable() : 削除。
・アクセラレーターを動的に追加するのは面倒。
「ってところかな」
『…… MFC の、って書いてあるってことは、今のだとこれは使えないって
ことだよね』
「そういうこと。というより、今の段階だとアクセラレーターそのものが使
えないし」
『げ、そうなの?』
「とりあえずは試してみようか。まずはリソースにアクセラレーターを追加
します。前回のメニューと同じように【 SimpleWindow リソース】の上で右
クリックして【挿入】を選んで」
『んで Accelerator を選んで【新規作成】だね。お、何もないアクセラ
レーター?』
「ここにテスト用のアクセラレーターを追加します。最初の行でダブルク
リックしてプロパティを出して」
『ほい』
「 ID は前回のメニューと同じ ID_MENU_TEST 、あとは好きでいいよ」
『じゃあキーは Ctrl + A っと。タイプは【仮想キー】だよね』
「そう、まぁデフォルトがそうだろうからそのままで。あと、このアクセラ
レーター全体のリソース ID は……」
『デフォルトだと IDR_ACCELERATOR1 だね』
「 IDA_MAIN にしておいた方がいいかな」
『……これ、どう変えるの?』
「ワークスペースの方の IDR_ACCELERATOR1 の上で右クリック」
『あ、プロパティってあった。っていうか、メニューもこうやって変えられ
るんだよね』
「でもメニューバーからの方が楽だし……さて!」
『さて?』
「 Version 9.11 ( No.172 ) で見たように、メニューもアクセラレーター
も、処理としては同じです」
『つまりコマンド系ってことだよね』
「そういうこと。というわけで、前回のメニューの時のをそのまま流用しま
す。ウィンドウプロシージャをこう書き換えて」
// ウィンドウプロシージャ。
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_COMMAND )
{
if( LOWORD( p_wParam ) == ID_MENU_TEST )
{
// ID_MENU_TEST メニュー。
OutputDebugString( "ID_MENU_TEST : 終了します。\n" );
PostQuitMessage( 0 );
return 0;
}
}
// 標準的な処理をします。
return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );
}
『……どこが違うの?』
「 OutputDebugString() の行を追加しただけ」
『あー。ってゆーか要らなくない?』
「うん、なくても問題ないかも。成功すればウィンドウが閉じるわけだし」
『ま、とりあえずいーや。で……今までの話を総合すると、まだダメなんだ
よね』
「そうなんです。ちゃんとアクセラレーターが効いていれば、 Ctrl + A を
押したときに、メニューと同じように WM_COMMAND が ID_MENU_TEST を持っ
て送られてくるはずなんだけど」
『ビルドして実行! やっぱ何も起きないね』
「それはなぜかっていうと、今の段階だと、ただ普通にキー入力されてるっ
てみなされてるから」
『つまり Ctrl + A って入力されたってメッセージが来るだけなのね』
「そう、だからそれを変換する必要があるんです。というわけで、メッセー
ジループをこう書き換えて」
// メッセージループ。
int MessageLoop( HINSTANCE p_hInstance )
{
BOOL bRes;
MSG stMsg;
int iAclTranslated;
// アクセラレーターをリソースから読み込みます。
HACCEL hAccel
= LoadAccelerators
( p_hInstance
, MAKEINTRESOURCE( IDA_MAIN )
);
// メッセージループです。
while( 1 )
{
// メッセージをキューから取り出します。
bRes = GetMessage( &stMsg, NULL, 0, 0 );
if (
( bRes == 0 )
||
( bRes == -1 )
)
{
// 終了するのでループから抜けます。
break;
}
iAclTranslated
= TranslateAccelerator( stMsg.hwnd, hAccel, &stMsg );
if( iAclTranslated != FALSE )
{
// 変換したのでメッセージ変換とディスパッチはしません。
continue;
}
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
}
return stMsg.wParam;
}
「さらに WinMain() で呼んでる部分をこう書き換えて」
return MessageLoop( p_hInstance );
『あ、インスタンスハンドルをメッセージループに渡すようにしたんだ』
「その理由は、アクセラレーターをリソースから読み込む時に必要だから」
// アクセラレーターをリソースから読み込みます。
HACCEL hAccel
= LoadAccelerators
( p_hInstance
, MAKEINTRESOURCE( IDA_MAIN )
);
「の部分」
『前回の LoadMenu() と似てるね』
「同じリソースを読み込む関数だからね。第1引数にインスタンスハンドル
を、第2引数にアクセラレーターのリソース ID を MAKEINTRESOURCE() に
渡したもの、を渡せばいいから」
『これを使ってるのが……』
iAclTranslated
= TranslateAccelerator( stMsg.hwnd, hAccel, &stMsg );
『だね』
「この API は、キー入力されたっていうメッセージを見つけたらそれを変
換してウィンドウプロシージャに送ってくれます」
『キー入力?』
「具体的に言うと、まずキー入力は WM_KEYDOWN 」
『あ、 Version 10.06 ( No.184 ) の TranslateMessage() の話の時に出て
きたのだね』
「そうそれ。このメッセージを受け取ると、アクセラレーターテーブルの中
に登録されているか調べます」
『で、 Ctrl + A だと』
「ウィンドウプロシージャに WM_COMMAND を直接送ります」
『もちろん ID_MENU_TEST を付けてだよね』
「もちろん。だからウィンドウプロシージャでそれを受けてあげれば……」
『そうだ、ビルドして実行、 Ctrl + A っと。おおっ! ウィンドウが閉じ
た! アウトプットウィンドウにも【ID_MENU_TEST : 終了します。】って
出てる!』
「というわけです」
『ま、ようするにメッセージを TranslateAccelerator() に渡すようにすれ
ば自動的に変換してくれるってわけね』
「まぁそれでもいいんだけど…… TranslateAccelerator() をもう少し詳し
くみておくね。第1引数は処理するウィンドウのハンドル」
『あ、 MSG 構造体ってウィンドウハンドルも持ってるんだね』
「第2引数はさっきロードしたアクセラレーターのハンドル。第3引数は
メッセージそのもの」
『で、戻り値を』
if( iAclTranslated != FALSE )
{
// 変換したのでメッセージ変換とディスパッチはしません。
continue;
}
『って感じにチェックしてるね』
「 TranslateAccelerator() は、メッセージを受け取ってウィンドウプロ
シージャに WM_COMMAND を送ると、それが処理するまで、つまりウィンド
ウプロシージャから抜けるまで返ってきません」
『え? つまり、メッセージループが止まるっていうこと?』
「そういうこと。で、処理されたあとに戻ってくるんだけど、それってつ
まり WM_KEYDOWN そのものが処理されたってことだよね」
『あ! そのままだと WM_KEYDOWN をもう一度ウィンドウプロシージャに
送っちゃう!』
「だとまずいからここでチェックしてるんです。 TranslateAccelerator()
はウィンドウプロシージャに無事遅れたら 0 以外を返すから」
「それを調べて…… continue ってなんだっけ」
『あ、教えてなかった……これは for や while の先頭に戻るものなんだけ
ど……次回に続く!』
「ええっ!?」
/*
Preview Next Story!
*/
『なんかいきなり続いたね』
「 continue ってちょっとややこしいからね」
『そんなに?』
「そんなでもないんだけど……ちょっとしっかり触れたくて」
『だから延びるのよー』
「ま、いまさらそれは考えないってことで」
『はいはい』
「というわけで次回」
< Version 10.11 プログラムの構造を考える >
『につづく!』
「そしてさらっと4度目の正月を迎えて……」
『あと何度迎えるやら……』