#pragma twice

KAB-studio > プログラミング > #pragma twice > 188 Version 10.10 アクセラレーターふたたび

#pragma twice 188 Version 10.10 アクセラレーターふたたび

前のページへ 表紙・目次へ 次のページへ

 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度目の正月を迎えて……」
『あと何度迎えるやら……』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。